Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8ab5cc6
Show workers a consumed signal's sequence and keep full task snapshot…
rmcdaniel Oct 23, 2026
75c775e
Record the dispatch-stamp difference the fuller task snapshots show
rmcdaniel Oct 23, 2026
8b0a879
Serve synchronous update validation like Server 2.11.1
rmcdaniel Oct 23, 2026
fe8269b
Refuse policy maps that are not JSON objects, like PHP #705
rmcdaniel Oct 23, 2026
f31c4c3
Send MySQL statement closes with the next command
rmcdaniel Oct 23, 2026
dc58f93
Keep explicit MySQL flushes and cache clears sending statement closes
rmcdaniel Oct 23, 2026
4d28b1e
Merge remote-tracking branch 'origin/main' into rust-325-mysql-stmt-c…
rmcdaniel Oct 23, 2026
faded35
Keep declared signals' record sequence empty under a condition wait
rmcdaniel Oct 24, 2026
64f5ec4
Merge remote-tracking branch 'origin/main' into rust-325-history-pres…
rmcdaniel Oct 24, 2026
3961dc2
Merge remote-tracking branch 'origin/main' into rust-325-update-valid…
rmcdaniel Oct 24, 2026
c800c79
Merge remote-tracking branch 'origin/main' into rust-325-policy-json
rmcdaniel Oct 24, 2026
76153d1
Merge remote-tracking branch 'origin/rust-325-mysql-stmt-close' into …
rmcdaniel Oct 24, 2026
9cbbb8b
Merge remote-tracking branch 'origin/rust-325-update-validation' into…
rmcdaniel Oct 24, 2026
3ff6ea6
Merge remote-tracking branch 'origin/rust-325-policy-json' into rust-…
rmcdaniel Oct 24, 2026
069270b
Merge remote-tracking branch 'origin/rust-325-history-presentation' i…
rmcdaniel Oct 24, 2026
31467e5
CI: cancel a superseded pull request run of the fixture and PHPUnit w…
rmcdaniel Oct 24, 2026
d1372fb
Server parity: give the update-validation probe a 90-second limit
rmcdaniel Oct 24, 2026
38b8267
Merge remote-tracking branch 'origin/rust-325-ci-cancel-superseded' i…
rmcdaniel Oct 24, 2026
180d4e3
Retry typed lock-pressure refusals in waiting update requests; let su…
rmcdaniel Oct 24, 2026
1fd9e54
Record SQLite update validation apart, with PHP maintenance stopped f…
rmcdaniel Oct 24, 2026
9d2ab68
Retry documented lock-pressure poll refusals in the Prometheus and le…
rmcdaniel Oct 24, 2026
9bb9cb9
Address Sonar's shell findings in the SQLite update-validation scripts
rmcdaniel Oct 24, 2026
ef5a6be
Report the measured shutdown time when the SDK pressure window is missed
rmcdaniel Oct 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/phpunit-feature.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ jobs:
- name: Require safe PHP image build context
run: node --test tests/Unit/PhpImageContextTest.mjs

- name: Restore PHP's SQLite maintenance loop on every path
run: node --test tests/Unit/PhpMaintenanceWindowTest.mjs

- name: Validate embedded Workflow package authority
run: php scripts/ci/test-workflow-package-authority.php

Expand Down
22 changes: 21 additions & 1 deletion .github/workflows/server-parity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ on:
- tests/Unit/ServerParityRepairPassContractTest.mjs
- tests/Unit/ServerParityClusterInfoContractTest.mjs
- tests/Unit/ServerParityNamespaceDeleteContractTest.mjs
- tests/Unit/ServerParityUpdateValidationContractTest.mjs
- tests/Unit/ServerParityFixtureSelectionTest.mjs
- tests/Unit/ServerParityWaitingHistoryContractTest.mjs
- tests/Unit/ServerParityTaskRetryContractTest.mjs
- tests/Unit/ServerParityPublishedPatchContractTest.mjs
Expand Down Expand Up @@ -99,6 +101,8 @@ on:
- tests/Unit/ServerParityRepairPassContractTest.mjs
- tests/Unit/ServerParityClusterInfoContractTest.mjs
- tests/Unit/ServerParityNamespaceDeleteContractTest.mjs
- tests/Unit/ServerParityUpdateValidationContractTest.mjs
- tests/Unit/ServerParityFixtureSelectionTest.mjs
- tests/Unit/ServerParityWaitingHistoryContractTest.mjs
- tests/Unit/ServerParityTaskRetryContractTest.mjs
- tests/Unit/ServerParityPublishedPatchContractTest.mjs
Expand All @@ -114,6 +118,13 @@ on:
- rust/**
workflow_dispatch:

# A newer push to the same pull request supersedes the run in progress, so a
# replaced head does not keep holding the organization's shared runners.
# Manual dispatches always finish.
concurrency:
group: shared-server-fixtures-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read

Expand Down Expand Up @@ -1522,7 +1533,7 @@ jobs:
-e COMPOSER_HOME=/tmp/composer -v "$PWD:/source" -w /source "$image" \
install --working-dir=scripts/conformance/server-parity --no-interaction --no-progress
docker run --rm --user=1000:1000 --entrypoint node -v "$PWD:/source:ro" -w /source \
"$image" --test tests/Unit/ServerParityRunnerTest.mjs tests/Unit/ServerParityCooperativeContractTest.mjs tests/Unit/ServerParityChildCancellationContractTest.mjs tests/Unit/ServerParityScheduleContractTest.mjs tests/Unit/ServerParityVisibilityContractTest.mjs tests/Unit/ServerParityAdmissionContractTest.mjs tests/Unit/ServerParityRoleTokenContractTest.mjs tests/Unit/ServerParityWorkerDeregistrationContractTest.mjs tests/Unit/ServerParitySearchAttributeContractTest.mjs tests/Unit/ServerParityWorkerListingContractTest.mjs tests/Unit/ServerParityWorkerReplacementContractTest.mjs tests/Unit/ServerParityPatchDeploymentContractTest.mjs tests/Unit/ServerParityWaitingHistoryContractTest.mjs tests/Unit/ServerParityTaskRetryContractTest.mjs tests/Unit/ServerParityPublishedPatchContractTest.mjs tests/Unit/ServerParityPublishedPatchTransportTest.mjs tests/Unit/ServerParityRecordFileTest.mjs tests/Unit/ServerParityLateWorkflowCompletionContractTest.mjs tests/Unit/ServerParityStorageTestContractTest.mjs tests/Unit/ServerParitySchedulePoliciesContractTest.mjs tests/Unit/ServerParitySignalsBeforeWorkerContractTest.mjs tests/Unit/ServerParityScheduleCatalogContractTest.mjs tests/Unit/ServerParityOperatorMetricsContractTest.mjs tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs tests/Unit/ServerParityServiceCatalogContractTest.mjs tests/Unit/ServerParitySystemMetricsContractTest.mjs tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs tests/Unit/ServerParityPrometheusMetricsContractTest.mjs tests/Unit/ServerParityPayloadCleanupContractTest.mjs tests/Unit/ServerParityRepairPassContractTest.mjs tests/Unit/ServerParityClusterInfoContractTest.mjs tests/Unit/ServerParityNamespaceDeleteContractTest.mjs > parity-evidence/comparator-tests.txt
"$image" --test tests/Unit/ServerParityRunnerTest.mjs tests/Unit/ServerParityCooperativeContractTest.mjs tests/Unit/ServerParityChildCancellationContractTest.mjs tests/Unit/ServerParityScheduleContractTest.mjs tests/Unit/ServerParityVisibilityContractTest.mjs tests/Unit/ServerParityAdmissionContractTest.mjs tests/Unit/ServerParityRoleTokenContractTest.mjs tests/Unit/ServerParityWorkerDeregistrationContractTest.mjs tests/Unit/ServerParitySearchAttributeContractTest.mjs tests/Unit/ServerParityWorkerListingContractTest.mjs tests/Unit/ServerParityWorkerReplacementContractTest.mjs tests/Unit/ServerParityPatchDeploymentContractTest.mjs tests/Unit/ServerParityWaitingHistoryContractTest.mjs tests/Unit/ServerParityTaskRetryContractTest.mjs tests/Unit/ServerParityPublishedPatchContractTest.mjs tests/Unit/ServerParityPublishedPatchTransportTest.mjs tests/Unit/ServerParityRecordFileTest.mjs tests/Unit/ServerParityLateWorkflowCompletionContractTest.mjs tests/Unit/ServerParityStorageTestContractTest.mjs tests/Unit/ServerParitySchedulePoliciesContractTest.mjs tests/Unit/ServerParitySignalsBeforeWorkerContractTest.mjs tests/Unit/ServerParityScheduleCatalogContractTest.mjs tests/Unit/ServerParityOperatorMetricsContractTest.mjs tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs tests/Unit/ServerParityServiceCatalogContractTest.mjs tests/Unit/ServerParitySystemMetricsContractTest.mjs tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs tests/Unit/ServerParityPrometheusMetricsContractTest.mjs tests/Unit/ServerParityPayloadCleanupContractTest.mjs tests/Unit/ServerParityRepairPassContractTest.mjs tests/Unit/ServerParityClusterInfoContractTest.mjs tests/Unit/ServerParityNamespaceDeleteContractTest.mjs tests/Unit/ServerParityUpdateValidationContractTest.mjs tests/Unit/ServerParityFixtureSelectionTest.mjs > parity-evidence/comparator-tests.txt
docker run --rm --user=1000:1000 --cpus=1 --memory=256m --memory-swap=256m \
--network=none --entrypoint node -v "$PWD:/source:ro" -w /source \
"$image" --test tests/Unit/ServerParityCodecDeadlineLoadTest.mjs > parity-evidence/codec-load-tests.txt
Expand Down Expand Up @@ -1581,6 +1592,7 @@ jobs:
-v "$PWD:/source:ro" -v "$PWD/parity-evidence:/evidence" -w /source \
-e DW_PARITY_TOKEN=parity-test-token -e DW_PARITY_RUNNER_REVISION -e DW_PARITY_CLI_PHAR "$image" scripts/conformance/server-parity.mjs record \
--mode http --url http://parity-server:8080 --target php --output /evidence/php.json \
--exclude-fixture tests/Fixtures/ServerParity/update-validation.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-insertion-activity-pending.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-insertion-activity-completed.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-created-activity-pending.json \
Expand All @@ -1593,6 +1605,7 @@ jobs:
-e DB_CONNECTION=sqlite -e DB_DATABASE=/state/embedded.sqlite -e DW_MODE=embedded \
-e CACHE_STORE=file -e QUEUE_CONNECTION=database -e DW_PARITY_RUNNER_REVISION "$image" scripts/conformance/server-parity.mjs record \
--mode embedded --application-root /app --target embedded --output /evidence/embedded.json \
--exclude-fixture tests/Fixtures/ServerParity/update-validation.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-insertion-activity-pending.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-insertion-activity-completed.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-created-activity-pending.json \
Expand All @@ -1605,6 +1618,7 @@ jobs:
-v "$PWD:/source:ro" -v "$PWD/parity-evidence:/evidence" -w /source \
-e DW_PARITY_TOKEN=parity-test-token -e DW_PARITY_RUNNER_REVISION -e DW_PARITY_CLI_PHAR "$image" scripts/conformance/server-parity.mjs record \
--mode http --url http://parity-rust:8080 --target rust --output /evidence/rust.json \
--exclude-fixture tests/Fixtures/ServerParity/update-validation.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-insertion-activity-pending.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-insertion-activity-completed.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-created-activity-pending.json \
Expand All @@ -1616,13 +1630,18 @@ jobs:
docker run --rm --user=1000:1000 --entrypoint node \
-v "$PWD:/source:ro" -v "$PWD/parity-evidence:/evidence" -w /source "$image" \
scripts/conformance/server-parity.mjs compare /evidence/php.json /evidence/rust.json /evidence/embedded.json \
--exclude-fixture tests/Fixtures/ServerParity/update-validation.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-insertion-activity-pending.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-insertion-activity-completed.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-created-activity-pending.json \
--include-fixture tests/Fixtures/ServerParityPending/patch-created-activity-completed.json \
--include-fixture tests/Fixtures/ServerParityPending/workflow-waiting-history-type.json \
--include-fixture tests/Fixtures/ServerParityPending/workflow-waiting-history-message.json \
--include-fixture tests/Fixtures/ServerParityPending/workflow-task-transient-retry.json > parity-evidence/comparison.json || compare_status=$?
# Server #723: update-validation records apart from the main corpus, and
# its published PHP record runs with PHP's maintenance loop stopped.
update_validation_status=0
bash scripts/conformance/server-parity/update-validation-sqlite.sh http://parity-server:8080 http://parity-rust:8080 || update_validation_status=$?
bash scripts/conformance/server-parity/published-sdk-patch-profile.sh sqlite http://parity-server:8080 http://parity-rust:8080
bash scripts/conformance/server-parity/repeated-patch-profile.sh sqlite http://parity-server:8080 http://parity-rust:8080
bash scripts/conformance/server-parity/legacy-marker-profile.sh sqlite http://parity-server:8080 http://parity-rust:8080
Expand Down Expand Up @@ -1737,6 +1756,7 @@ jobs:
# check has run, so one difference cannot hide the rest.
test "$native_record_status" -eq 0
test "$compare_status" -eq 0
test "$update_validation_status" -eq 0
- name: Compare separate configured role-token cohort
env:
RESOURCE_SCOPE: dw-parity-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# October 23, MySQL statement closes and late timers

- **Found by.** Server #714. Twice on October 23, the hosted MariaDB 10.11
`codec-deadline-health` cell recorded a native timer more than the 1 s
maximum late. In one, `TimerFired` came 1.098 s after `fire_at`. MySQL 8.0
and 8.4 cells fail the same way, less often.
- **Reproduction.** A disposable 4-vCPU VM ran the unchanged cell on main
`f67f2816e`, with temporary timing in a local-only build:
- MariaDB failed 2 of the first 8 cells, with timers 1.228 s and 1.213 s
late;
- PostgreSQL 17 and SQLite passed all 8 cells, at most 171 ms late.
- **Attribution.** Every late timer waited behind the global transition lock:
- In the worst MariaDB cell, the tick's activity-deadline sweep waited
543 ms behind a workflow completion that held the lock 690 ms. The sweep
then held it 407 ms itself, and the timer fired 1.228 s late.
- Ordinary transitions held the lock 300 ms to 1 s. With every statement
logged by MariaDB, those transactions spent a median of 5 ms in the
database (p90 9 ms) across their 30 to 48 statements.
- Native was not CPU-starved. Wake-up probes on an async task and an OS
thread rarely saw 50 ms delays, and giving native 2 CPUs left the holds
unchanged.
- Timestamps on each statement inside the transactions showed gaps of
40 to 100 ms spread across ordinary statements, many near 58 ms. That is
Nagle's algorithm waiting for a delayed acknowledgement.
- **Cause.** `COM_STMT_CLOSE` has no reply. The published `sqlx-mysql` 0.9.0
flushes it in its own write: when its 100-entry statement cache evicts a
statement, and after executing a statement it does not cache. Before the
next command it also flushes anything still buffered. The next command is
then a second small write while the close is still unacknowledged, so the
kernel holds it about 40 ms. SQLx sets no `TCP_NODELAY`; PHP's mysqlnd does.
PostgreSQL's driver sends its closes together with the next request.
- **Change.** In the vendored driver, `MySqlStream::queue_packet` buffers each
statement close with sequence number 0. On the path before the next command,
`wait_until_ready` no longer flushes a buffer that holds only replyless
closes, so they leave in the same write as that command. `Connection::flush`
and `clear_cached_statements` still send queued closes before returning;
Security's review of the first revision found that they did not. Packets,
order and server behavior are unchanged. The patch record is
`rust/vendor/sqlx-mysql/DURABLE-PATCH.md`.
- **Tests.** A proxy records the TCP read chunks it receives from the client.
TCP is a byte stream, so a chunk boundary shows bytes that arrived
separately.
- `qualification_statement_close_leaves_with_the_next_command` uses a
one-entry statement cache and one uncached statement, so both close paths
are exercised. With the fix it passes on MySQL 8.0.46 and MariaDB 10.11 in
0.01 s. With the published driver it fails on both: every close arrives
as a chunk of its own, and the same 14 statements take 0.6 s.
- `qualification_explicit_flush_sends_queued_statement_closes`: a queued
close stays buffered until `flush`, which sends it with no later command
and leaves `should_flush` false.
- `qualification_clearing_the_statement_cache_closes_them_now`:
`clear_cached_statements` sends all three closes before it returns. On
MySQL 8.0 with the performance schema on, the server's statements for the
connection drop from three to none.
- Both lifecycle tests fail with the first revision (`f31c4c361`) on MySQL
8.0 and MariaDB, and pass with this one. The existing cancelled-boundary
qualification still passes.
- **Hosted-cell A/B on the VM.** Interleaved cells with the spec and limits
unchanged. Both builds were instrumented: one without the driver change, one
with its first revision. That revision already buffered the closes, but
`wait_until_ready` still flushed them before draining.

| | Cells passed | Timer lateness | Holds of 300 ms or more |
| --- | --- | --- | --- |
| MariaDB without the fix | 3 of 6 | 190 to 1045 ms | 54 |
| MariaDB with the fix | 6 of 6 | 49 to 289 ms | 0 |
| MySQL 8.0 and 8.4 without the fix | 5 of 8 | 138 to 1099 ms | 77 |
| MySQL 8.0 and 8.4 with the fix | 8 of 8 | 116 to 299 ms | 3 |

Clean release builds of main and of this branch are compared across all five
families in the PR's evidence comment.
33 changes: 33 additions & 0 deletions docs/rust-server-port-checkpoints/2026-10-23-policy-map-json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# October 23, policy maps must be JSON objects

- **Found by.** PHP Server #705 (merged to main at `f67f2816e`, not yet in a
published image). Published PHP turned malformed admission-policy JSON
into an empty policy, so a configured cap could disappear without notice.
Native had the same defect.
- `DW_TASK_QUEUE_ADMISSION_OVERRIDES` and
`DW_NAMESPACE_ADMISSION_OVERRIDES` accepted malformed JSON.
- All five maps native refuses accepted `null`, `[]`, booleans and
numbers.
- The external-payload namespace overrides, which native honors, became
an empty policy when malformed.
- **Change.** The six maps PHP now reads with `EnvAuditor::jsonObject` are
read by the same rule in `config_audit::json_object`:
- the value is either unset or raw text that decodes to a JSON object;
- Laravel's `env()` conversions do not apply, so `null`, `(null)`,
`empty`, quoted text and an empty value are refused;
- a set `DW_*` name wins over its legacy name even when the `DW_*` value
is invalid;
- the startup refusal names the variable it read, never the value.

Native still refuses a non-empty object in the five maps it has no policy
for. It still honors `DW_EXTERNAL_PAYLOAD_NAMESPACE_OVERRIDES`, and the
external payload quota now reads that value with the same decoder.
- **Reference.** This follows unreleased PHP source, not the published
2.11.1 image. For any valid configuration the two behave the same. The
only values refused are ones published PHP silently discarded and the
next PHP image refuses at boot.
- **Test.** `policy_maps_must_be_json_objects_like_php_705` covers all six
maps under both names, 17 invalid values, padded whitespace around `{}`,
DW-over-legacy precedence in both directions, and that the refusal never
repeats the value. The shipped Compose and Helm values (`{}` for the
durable maps) still start.
Loading
Loading