Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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: 2 additions & 1 deletion .github/workflows/server-parity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ on:
- tests/Unit/ServerParityWorkflowPollRequestContractTest.mjs
- tests/Unit/ServerParityActivityCancellationAckContractTest.mjs
- tests/Unit/ServerParityServiceCatalogContractTest.mjs
- tests/Unit/ServerParityWorkflowImportContractTest.mjs
- tests/Unit/ServerParitySystemMetricsContractTest.mjs
- tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs
- tests/Unit/ServerParityPrometheusMetricsContractTest.mjs
Expand Down Expand Up @@ -1800,7 +1801,7 @@ jobs:
bash scripts/ci/run-composer.sh --rm -v "$PWD:/source" -w /source -- \
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/ServerParitySdkDirectionLanesTest.mjs tests/Unit/ServerParityCodecDeadlineCellsTest.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/ServerParityFailureLocationTest.mjs tests/Unit/ServerParityFailureExcerptTest.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/ServerParityActivityCancellationAckContractTest.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/ServerParityMessageStreamsContractTest.mjs tests/Unit/ServerParityWorkflowPollRequestContractTest.mjs tests/Unit/ServerParityUpdateValidationContractTest.mjs tests/Unit/ServerParityFixtureSelectionTest.mjs tests/Unit/ServerParityUpdateValidationRetryTest.mjs tests/Unit/ServerParityKilledAuthorPollTest.mjs tests/Unit/ServerParityImagePullTest.mjs tests/Unit/ServerParityWorkerSessionsContractTest.mjs tests/Unit/ServerParityWorkerSessionActivitiesContractTest.mjs > parity-evidence/comparator-tests.txt
"$image" --test tests/Unit/ServerParityRunnerTest.mjs tests/Unit/ServerParitySdkDirectionLanesTest.mjs tests/Unit/ServerParityCodecDeadlineCellsTest.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/ServerParityFailureLocationTest.mjs tests/Unit/ServerParityFailureExcerptTest.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/ServerParityActivityCancellationAckContractTest.mjs tests/Unit/ServerParityServiceCatalogContractTest.mjs tests/Unit/ServerParityWorkflowImportContractTest.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/ServerParityMessageStreamsContractTest.mjs tests/Unit/ServerParityWorkflowPollRequestContractTest.mjs tests/Unit/ServerParityUpdateValidationContractTest.mjs tests/Unit/ServerParityFixtureSelectionTest.mjs tests/Unit/ServerParityUpdateValidationRetryTest.mjs tests/Unit/ServerParityKilledAuthorPollTest.mjs tests/Unit/ServerParityImagePullTest.mjs tests/Unit/ServerParityWorkerSessionsContractTest.mjs tests/Unit/ServerParityWorkerSessionActivitiesContractTest.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
24 changes: 16 additions & 8 deletions docs/rust-server-port.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,12 @@ Updated October 24, 2026.
([#737](https://github.com/durable-workflow/server/issues/737)).

No expectation is edited to make native pass.
- **Parity surface.** Native serves 138 of the 175 API route methods in
- **Parity surface.** Native serves 139 of the 175 API route methods in
published 2.11.1's route table, with message streams, update validation,
activity cancellation acknowledgement and worker sessions. An execution
test sends each one through the native router, and the ones still
answering `501 rust_capability_not_implemented` must match
activity cancellation acknowledgement, worker sessions and embedded v2
workflow import. An execution test sends each one through the native
router, and the ones still answering `501 rust_capability_not_implemented`
must match
[`unserved-php-routes.v2.11.1.txt`](../rust/tests/fixtures/unserved-php-routes.v2.11.1.txt).
- **Next action.** Land the items above in order, keep porting the unserved
routes fixture first, and package the native binary into the server image.
Expand Down Expand Up @@ -132,12 +133,15 @@ any other unknown path or method answers PHP's own 404 or 405
`repair-pass` fixture; a recovery native does not perform yet is
reported as its candidate's failure, with 207.
- **Worker protocol features.**
- Worker sessions.
- 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
- (Worker sessions are served: create, heartbeat, close, list and detail,
with the `worker-sessions` and `worker-session-activities` fixtures.)
- (Activity cancellation acknowledgement is served:
`POST /api/worker/activity-tasks/{task}/acknowledge-cancellation`, with the
`activity-cancellation-acknowledgement` fixture. So is lease renewal, from
Server 2.11: `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
Expand All @@ -146,7 +150,11 @@ any other unknown path or method answers PHP's own 404 or 405
`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.
- Waterline v1 import. (Embedded v2 import is served:
`POST /api/workflows/import/embedded-v2`. Unlike published PHP, native
refuses a bundle whose rows reference rows outside it, links only runs of
one namespace, and reads the signing key only from the request body
([#765](https://github.com/durable-workflow/server/issues/765)).)
- **Other families.**
- Message streams are served
([checkpoint](rust-server-port-checkpoints/2026-10-23-message-streams.md)),
Expand Down
2 changes: 1 addition & 1 deletion rust/src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const PHP_BASE64: GeneralPurpose = GeneralPurpose::new(

/// Decodes a blob as published PHP does: ASCII whitespace is skipped, and
/// any padding must be exactly what the final quantum needs.
fn php_base64(blob: &str) -> Result<Vec<u8>, base64::DecodeError> {
pub(crate) fn php_base64(blob: &str) -> Result<Vec<u8>, base64::DecodeError> {
let compact: String = blob.chars().filter(|c| !matches!(c, ' ' | '\t' | '\n' | '\r')).collect();
let unpadded = compact.trim_end_matches('=');
let padding = compact.len() - unpadded.len();
Expand Down
4 changes: 3 additions & 1 deletion rust/src/runtime/activity_deadlines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,10 @@ where
let (Some(task), Some(execution_id)) = (task, execution_id) else {
return Ok(Skipped("activity_execution_missing"));
};
let Some(activity) = Self::query("SELECT * FROM activity_executions WHERE id=$1")
// Only the task's own run's activity (Server #765).
let Some(activity) = Self::query("SELECT * FROM activity_executions WHERE id=$1 AND workflow_run_id=$2")
.bind(&execution_id)
.bind(DB::string(&task, "workflow_run_id")?)
.fetch_optional(&mut **tx)
.await?
else {
Expand Down
9 changes: 6 additions & 3 deletions rust/src/runtime/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ pub(super) trait Backend: Database {
const RUN_COMMANDS_SQL: &str =
super::store::run_commands_sql!(
"substr(s.arguments,1,30)='dw-native-external-payload-v1:'",
"substr(c.payload,1,30)='dw-native-external-payload-v1:'"
"substr(c.payload,1,30)='dw-native-external-payload-v1:'",
"substr(c.payload,1,23)='dw-external-payload:v1:'"
);
const SELECTION_RESOLUTION_SQL: &str = "SELECT id,payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type='SelectionResolved' AND payload->>'selection_group_id'=$2 ORDER BY sequence LIMIT 1";
// Frozen run command/task repair counters use each backend's actual width.
Expand Down Expand Up @@ -205,7 +206,8 @@ ORDER BY t.available_order,t.id LIMIT 100
const RUN_COMMANDS_SQL: &str =
super::store::run_commands_sql!(
"substr(s.arguments,1,30) COLLATE BINARY='dw-native-external-payload-v1:'",
"substr(c.payload,1,30) COLLATE BINARY='dw-native-external-payload-v1:'"
"substr(c.payload,1,30) COLLATE BINARY='dw-native-external-payload-v1:'",
"substr(c.payload,1,23) COLLATE BINARY='dw-external-payload:v1:'"
);
const HISTORY_PAGE: &str = "SELECT * FROM (SELECT *,SUM(length(CAST(payload AS BLOB))) OVER (ORDER BY sequence) AS page_bytes FROM workflow_history_events WHERE workflow_run_id=$1 AND sequence>$2 ORDER BY sequence LIMIT $3) AS page WHERE page_bytes<=8388608 ORDER BY sequence";
// Normalize only the two supported UTC storage shapes. Removing trailing
Expand Down Expand Up @@ -487,7 +489,8 @@ impl Backend for MySql {
const RUN_COMMANDS_SQL: &str =
super::store::run_commands_sql!(
"CAST(SUBSTRING(s.arguments,1,30) AS BINARY)='dw-native-external-payload-v1:'",
"CAST(SUBSTRING(c.payload,1,30) AS BINARY)='dw-native-external-payload-v1:'"
"CAST(SUBSTRING(c.payload,1,30) AS BINARY)='dw-native-external-payload-v1:'",
"CAST(SUBSTRING(c.payload,1,23) AS BINARY)='dw-external-payload:v1:'"
);
// First use the preserved run/sequence index to materialize only IDs and
// byte counts for this bounded page. Sorting JSON payloads in the window
Expand Down
Loading
Loading