feat(cluster): php.ini configuration, execution profiles and ScyllaDB settings - #148
Merged
CodeLieutenant merged 9 commits intoAug 10, 2026
Merged
Conversation
|
Tick the box to add this pull request to the merge queue (same as
|
… settings Add a configuration layer for the driver and expose a large part of the C driver surface that the extension never reached. php.ini configuration (50 directives) - Seeds for every Cluster\Builder default. Each with*() method still wins. - Bounds on the per-worker persistent caches, plus cassandra.allow_persistent as an operator kill switch. - All directives are PHP_INI_SYSTEM: the values form the persistent-cluster cache key, so a request-scoped ini_set() would grow the cache without bound. - A bad value is ignored in favour of the documented default and writes one E_WARNING, so ini_get() and phpinfo() always report what the driver uses. Execution profiles - New Cassandra\ExecutionProfile covering all sixteen driver settings. - Register with Cluster\Builder::withExecutionProfile(), select with the third argument of execute() and executeAsync(). - Both accept a string or an enum case. A string-backed enum contributes its value, any other enum its case name. - Each setter folds its arguments into a fingerprint that reaches the cluster cache key, because CassExecProfile is opaque and two clusters differing only in a profile would otherwise share one CassCluster. ScyllaDB and connection settings - Rack-aware load balancing, application name and version, exponential reconnect with jitter, speculative execution, and event-loop tuning. - Fourteen further cluster directives, each defaulting to the C driver's own value so nothing changes until an operator sets one. Breaking - Default consistency is now LOCAL_QUORUM, was LOCAL_ONE. - The persistent caches are bounded by default: 16 clusters, 16 sessions and 1000 prepared statements, sized from measured per-entry cost. Both are reversible with one php.ini line. See CHANGELOG.md. Fixes - DefaultCluster::connect() leaked the connect CassFuture on the non-persistent path. Confirmed with leaks: one 192-byte root leak per call. - withConnectionHeartbeatInterval() and withTCPKeepalive() passed a value 1000 times too large. Both C functions take seconds; the builder stored milliseconds. A 30 second heartbeat asked for 30000 seconds. - cassandra.log = syslog wrote a file named "syslog" instead of using syslog(3). Dependencies - The Cassandra backend builds from apache/cassandra-cpp-driver. - All three drivers track their default branch. This commit also carries in-progress release, packaging and CI work that was already present in the working tree, and removes the legacy tests-old tree. # Conflicts: # website/.vitepress/config.mts # website/guide/observability.md # website/reference/cluster-builder.md
…iles Adds the connection lifetime, prepared statement, protocol/routing and request tracing directives to both the guide and cassandra.ini.in, so all 50 directives are documented in both places. Adds a guide page for execution profiles covering registration, selection by string or enum case, all sixteen profile settings, the copy-on-build rule and the effect on the persistent cluster cache. Wires it into the sidebar, the Cluster\Builder reference and the Session reference, whose execute() and executeAsync() signatures now show the third argument. # Conflicts: # website/.vitepress/config.mts
CodeLieutenant
force-pushed
the
feat/driver-configuration-and-execution-profiles
branch
from
August 9, 2026 23:16
2e958ab to
5903a2c
Compare
CI failed with "stub file not found: src/ExecutionProfile/ExecutionProfile.stub.php". The file was on disk but never entered the repository. The cause is the `ex*.php` rule in .gitignore. The pattern has no slash, so git applies it at every depth, and macOS sets core.ignorecase. The rule therefore matched `ExecutionProfile.stub.php` and `git add` skipped it without a message. The same rule also hid tests/Unit/ExecutionOptionsTest.php and tests/Unit/ExecutionProfileTest.php. Anchor the rule to the repository root, where the old phpize boilerplate put it. Add the three files it hid.
Three unrelated breaks, one per failing job group. **cassandra backend — build error.** `cass_cluster_set_load_balance_rack_aware` exists only in the ScyllaDB drivers. apache/cassandra-cpp-driver does not declare it, so every cassandra job failed at src/Cluster/Builder.c:260 with an implicit declaration. Guard the call and warn at build() time, the same way an unsupported hostname resolution already does. cpp-rs-driver declares the function, so only the cassandra backend loses the policy. **scylla-rust backend — configure error.** FindCPPDriver.cmake looked for the pkg-config module `scylla-cpp-driver`, but cpp-rs-driver installs `scylladb.pc` and `scylladb_static.pc`. See scylla-rust-wrapper/CMakeLists.txt in that project. Use the correct names and correct the comment that claimed both ScyllaDB drivers share one module name. **clang-tidy — missing generated header.** The lint job built a hardcoded list of module targets to materialise *_arginfo.h. The list did not include the new execution_profile module, so clang-tidy could not find ExecutionProfile_arginfo.h. Add a `generated-sources` aggregate target in cmake/GenStubs.cmake that every generated file attaches to, and build that instead. A new module now needs no edit to the workflow. Also stop hiding a generation failure behind `|| true`. A failure there must fail the lint job, not turn into a confusing "file not found".
The scylla-rust CI jobs failed to compile. cpp-rs-driver declares none of cass_cluster_set_new_request_ratio, _queue_size_io, _monitor_reporting_interval, _prepare_on_up_or_add_host, _no_compact, _tracing_consistency, _tracing_max_wait_time or _tracing_retry_wait_time, so every call was an implicit function declaration, which the build treats as an error. Wrap those eight in the existing PHP_SCYLLADB_BACKEND_SCYLLA_RUST guard, the same pattern withMaxConnectionsPerHost already uses. The directives stay registered on that backend and simply keep their driver default, which is why this needs no INI change. Checked the whole branch against the cpp-rs-driver header rather than fixing only what the log showed: these eight are the only additions it lacks. Execution profiles, rack-aware routing, application name and version, exponential reconnect, speculative execution, coalesce delay and local address are all declared there. Verified by compiling Cluster/Builder.c both ways and reading the symbol table: 8 of 8 referenced without the macro, 0 of 8 with it.
The PR had gone CONFLICTING, which stops GitHub computing a merge commit, so no pull_request workflow ran at all on the last push. Merging trunk restores that. Two conflicts, both additive: - CHANGELOG.md: kept the Rows::wasApplied() entry from trunk alongside the configuration and execution profile entries from this branch. - .github/workflows/release.yml: trunk adds an ide-stubs job, this branch adds php-manylinux and build-manylinux. Kept all three and made the release job depend on build, build-manylinux, ide-stubs and tag. Merged rather than rebased on purpose: the branch carries commits that are not mine, and rewriting those to force-push is the worse trade.
The scylla-rust jobs still failed to compile after the previous fix, on a different set of functions. cpp-rs-driver up to v1.0.x declared these seven but never implemented them meaningfully; its master branch removed the declarations outright, so moving the pin from v1.0.0 to master turned every call site into an implicit declaration: cass_keyspace_meta_field_by_name cass_table_meta_field_by_name cass_column_meta_field_by_name cass_iterator_get_meta_field_name cass_iterator_get_meta_field_value cass_iterator_fields_from_table_meta cass_iterator_fields_from_materialized_view_meta Add include/php_scylladb_rs_compat.h, which supplies inert fallbacks under PHP_SCYLLADB_BACKEND_SCYLLA_RUST only. One header beats scattering guards through six files, and it keeps the call sites readable. Behaviour on that backend is unchanged, because it was already empty: the functions were listed under "Functions intentionally not implemented" and every call site already null-checks the pointer or iterator, so schema field introspection yields empty results exactly as before. Verified by compiling all 152 translation units against the real cpp-rs-driver master header rather than against a -D flag: the local C/C++ driver still declares these, so a define alone proves nothing. Zero errors.
…as no symbol The scylla-rust jobs got past compilation after the last fix and then died at runtime on every test: php: symbol lookup error: cassandra.so: undefined symbol: cass_cluster_set_prepare_on_all_hosts cpp-rs-driver declares this one in its header but ships no implementation, so it compiles and only fails when the loader resolves it. A header diff cannot see that, which is why the previous round missed it. The authoritative list is scylla-rust-wrapper/src/api.rs, an explicit manifest where unsupported entries are commented out and tagged UNIMPLEMENTED. Checked every symbol this branch adds against it: exactly nine are unimplemented, and all nine now sit inside the PHP_SCYLLADB_BACKEND_SCYLLA_RUST guard. The remaining unimplemented symbols the extension calls are pre-existing schema metadata paths that trunk already ships. Docs corrected from eight affected directives to nine.
…g_settings Last failing test on the scylla-rust backend. cpp-rs-driver has an ABI bug: its header declares CASS_EXPORT CassError cass_execution_profile_set_latency_aware_routing_settings(...) but scylla-rust-wrapper/src/exec_profile.rs implements it returning nothing. The caller therefore reads whatever is in the return register, and ASSERT_SUCCESS turned that garbage into a RuntimeException. Drop the check on this one call. The setter only stores fields on a profile we just allocated and cannot fail on either driver, so nothing is lost. Audited the other 23 functions this extension wraps in ASSERT_SUCCESS against the Rust implementations: this is the only signature mismatch.
CodeLieutenant
deleted the
feat/driver-configuration-and-execution-profiles
branch
August 10, 2026 22:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Adds a configuration layer for the driver and exposes a large part of the C driver surface the extension never reached. Coverage goes from 356 to 435 of 621 public
cass_*functions, surfaced as 50php.inidirectives and a newExecutionProfileclass.php.ini configuration
Cluster\Builderdefault. Eachwith*()method still wins.cassandra.allow_persistentas an operator kill switch.PHP_INI_SYSTEMby design: the values form the persistent-cluster cache key, so a request-scopedini_set()would growEG(persistent_list)without bound.E_WARNING, soini_get()andphpinfo()always report what the driver actually uses.Execution profiles
All sixteen driver settings are covered. Name and selector take a string or an enum case: a string-backed enum contributes its value, any other enum its case name.
CassExecProfileis opaque, so each setter folds its arguments into a fingerprint that reaches the cluster cache key. Without that, two clusters differing only in a profile would share oneCassClusterand silently get the wrong settings.ScyllaDB and connection settings
Rack-aware load balancing, application name/version (visible in
system.clients), exponential reconnect with jitter, speculative execution, event-loop tuning, and fourteen further cluster directives that each default to the C driver's own value.Breaking changes
LOCAL_ONEcassandra.default_consistency = LOCAL_ONEcassandra.max_persistent_* = -1Caps are 16 clusters, 16 sessions, 1000 prepared statements, sized from measured per-entry cost: ~12 KB, ~2.2 MB plus 2 sockets per node, and ~7.4 KB.
Fixes found on the way
DefaultCluster::connect()leaked the connectCassFutureon the non-persistent path. Confirmed withleaks: one 192-byte root leak per call, now zero.withConnectionHeartbeatInterval()andwithTCPKeepalive()passed a value 1000x too large. Both C functions take seconds; the builder stored milliseconds, so a 30 second heartbeat asked for 30000 seconds and effectively disabled heartbeats.cassandra.log = syslogcreated a file namedsyslogin the working directory instead of usingsyslog(3).Verification
trunkand with these changes, so no regression.leaks: 0 bytes across the cap, rack-aware, exponential, speculative and profile paths.DebugPHP8.4NTS,DebugPHP8.5NTS,RelWithDebInfoPHP8.5NTS.APPLICATION_NAMEinsystem.clients.client_options, and cap enforcement.Reviewer notes
Rebased onto
trunk. The branch is now 0 commits behind. Three conflicts were resolved: the sidebar moved into the new versionedwebsite/.vitepress/versions.ts, the logging table mergedexpose_credentialswith the newsyslogtarget, and theCluster\Builderreference merged both sets of rows.Stale defect notes removed.
trunkdocuments the heartbeat and TCP keepalive unit bug as a Known defect in 1.4.x and tells readers to divide by 1000 as a workaround. This branch fixes the bug, so that advice would now produce values 1000 times too short. Those notes are replaced with an upgrade note inconnection-tuning.md, and the references inperformance.md,connecting.mdand the builder reference are removed.Mixed content. The working tree already had uncommitted work when this was written, and it is included here at the author's request: release/packaging/CI changes (
release.yml,compile-php.sh,FindLibGMP.cmake,README.md) and the removal of the legacytests-old/tree. Those are not part of the driver-configuration work and can be split out if you prefer.Not verified. Execution profiles have not run against a live cluster (the local ScyllaDB went down partway through). ZTS is untested locally; CI's
tsmatrix is the first real check.syslogdelivery is confirmed only in that it no longer creates a stray file or falls through to stderr.