Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 16 additions & 1 deletion .github/workflows/server-parity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ on:
- tests/Unit/ServerParityWorkerReplacementContractTest.mjs
- tests/Unit/ServerParityOperatorMetricsContractTest.mjs
- tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs
- tests/Unit/ServerParityWorkflowPollRequestContractTest.mjs
- tests/Unit/ServerParityServiceCatalogContractTest.mjs
- tests/Unit/ServerParitySystemMetricsContractTest.mjs
- tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs
Expand All @@ -43,6 +44,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 @@ -91,6 +94,7 @@ on:
- tests/Unit/ServerParityWorkerReplacementContractTest.mjs
- tests/Unit/ServerParityOperatorMetricsContractTest.mjs
- tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs
- tests/Unit/ServerParityWorkflowPollRequestContractTest.mjs
- tests/Unit/ServerParityServiceCatalogContractTest.mjs
- tests/Unit/ServerParitySystemMetricsContractTest.mjs
- tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs
Expand All @@ -99,6 +103,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 @@ -1529,7 +1535,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/ServerParityWorkflowPollRequestContractTest.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 @@ -1588,6 +1594,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 @@ -1600,6 +1607,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 @@ -1612,6 +1620,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 @@ -1623,13 +1632,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 @@ -1744,6 +1758,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
85 changes: 85 additions & 0 deletions docs/rust-server-port-checkpoints/2026-10-23-update-validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# October 23, synchronous update validation

- **The gap.** The Python SDK lets a workflow declare update validators. A
worker with validators registers only when the server advertises
`update_validation_tasks` and its `synchronous_update_validation` contract.
Native advertised neither, and refused a validated update with 422
`update_validation_not_implemented`. So a Python worker with validators
could not run against native.
- **How PHP 2.11.1 does it.** Learned from `ValidatedExternalWorkflowUpdateAdmission`
and `WorkflowUpdateValidationTaskBroker`, then observed live against the
published image.
- A worker declares `update_validators` per workflow type and advertises
the `update_validation_tasks` capability. The run's `WorkflowStarted`
records `declared_update_validators` when the worker's contract has that
field.
- For a live run with a complete durable contract and a declared update,
admission checks, in order:
1. a recorded receipt for the `request_id`;
2. whether the contract states validators at all, else 409
`update_validator_contract_missing`, for any remote update;
3. the update's arguments;
4. a `request_id`, which a validated update requires.

None of these refusals is recorded.
- The validation task is keyed by run, update name and `request_id`, and
holds a hash of the arguments. Different arguments under the same key
conflict with 409. A task that has finished replays its result. The
queue's fresh workers must include one that is capable and matches the
run, else 409 with a routing reason.
- The caller waits up to `DW_UPDATE_VALIDATION_TIMEOUT` seconds (10). A
worker claims the task through `POST /api/worker/update-validation-tasks/poll`
or the workflow poll with `task_kinds`. The claim lasts
`DW_UPDATE_VALIDATION_LEASE_TIMEOUT` seconds (5). The task carries the
run's replay history and history export.
- Approval admits the update with its id equal to the task id. A validator
rejection records a rejected command and update under the task id, with
`UpdateRejected`, and answers 422. Any other worker failure answers 409
and records nothing. Without an answer, the task times out with 504 and
a reason: not claimed, worker lost, or execution timeout.
- Approve and reject are fenced, in order: task found, not finished,
leased, owner, attempt, lease unexpired, worker fresh. An expired lease
can be claimed again with the next attempt.
- The multiplexed poll keeps a cursor per queue. It tries the kind the
cursor names first, and turns the cursor to the other kind after each
lease. Every workflow task names `task_kind: "workflow"`.
- **What native does now.**
- Admission follows the same order, with the same bodies, and waits
without holding a transaction. Approval re-enters admission with the
task id, after checking that the run is still current and the approval
still stands.
- The validation task poll, approve and reject routes, with Laravel's
request rules.
- The workflow poll validates `task_kinds`. With validation tasks
requested, it refuses a worker without the capability, answers
`no_workflow_capability` for a worker without workflow types, and
alternates kinds by the `workflow_task_poll_cursors` row.
- The capability, its contract and both timeouts (`DW_UPDATE_VALIDATION_*`,
now honored settings) are advertised.
- A validated task's payload carries the unsigned history export, as
`HistoryExport::forRun` builds it.
- **Native changes found on the way.**
- An accepted update's receipt no longer carries `accepted: true`. PHP's
result mapper sends `accepted` only when it is false.
- A worker contract without `update_validators` now makes a remote update
refuse with 409, as on PHP. Current PHP, Python and Rust SDK workers all
send the field.
- **How it was checked.**
- The `update-validation` fixture records 49 steps: refusals before
validation, a rejection through the multiplexed poll, an approval through
the dedicated poll with its fences, retries, three timeouts run side by
side, lease expiry and re-claim, a worker failure, multiplexed ordering
and every routing refusal.
- Two published PHP runs normalize identically. Native matches all 49
steps on SQLite. The contract test refuses 19 changed observations.
- Native integration tests cover the same paths on SQLite, PostgreSQL and
MySQL.
- **Not done yet** (Server #325 follow-ups):
- **Poll request ids.** PHP replays a leased validation task for a retried
`poll_request_id` and binds the request id to its task kinds. Native
does neither, so a retried multiplexed poll waits for the lease to
expire before the task is offered again.
- **Cluster info.** PHP's `workflow_update_runtime_contract` manifest and
its `service_mode_update_validation` block are not published yet; that
belongs to the cluster info work.
- **Takeover.** PHP data with validation tasks is still refused.
7 changes: 6 additions & 1 deletion docs/rust-server-port.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,18 @@ any other unknown path or method answers PHP's own 404 or 405
reported as its candidate's failure, with 207.
- **Worker protocol features.**
- Worker sessions.
- Update validation tasks.
- Local activities (prepare, checkpoint, recover, heartbeat, control,
outcome and cancellation acknowledgement).
- Cancellation scopes.
- Activity cancellation acknowledgement. (Lease renewal, from Server 2.11,
is served: `POST /api/worker/activity-tasks/{task}/renew-lease`, with the
`activity_lease_renewal` capability and the `activity-lease-renewal` fixture.)
- (Synchronous update validation is served: the validation task poll,
approve and reject routes, the multiplexed workflow poll with
`task_kinds`, and validator-gated update admission, with the
`update-validation` fixture. Native does not replay a validation task by
`poll_request_id` or bind a request id to its task kinds; see
`docs/rust-server-port-checkpoints/2026-10-23-update-validation.md`.)
- **Run operations.**
- Imports from embedded v2 and Waterline v1.
- **Other families.**
Expand Down
Loading
Loading