diff --git a/.github/workflows/server-parity.yml b/.github/workflows/server-parity.yml index 5440e0f6b..36bc297d0 100644 --- a/.github/workflows/server-parity.yml +++ b/.github/workflows/server-parity.yml @@ -2399,6 +2399,7 @@ jobs: --mode http --url http://parity-server:8080 --target php --output /evidence/php.json \ --exclude-fixture tests/Fixtures/ServerParity/service-calls.json \ --exclude-fixture tests/Fixtures/ServerParity/schedule-catalog.json \ + --exclude-fixture tests/Fixtures/ServerParity/namespace-delete-payloads.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 \ @@ -2413,6 +2414,7 @@ jobs: --mode embedded --application-root /app --target embedded --output /evidence/embedded.json \ --exclude-fixture tests/Fixtures/ServerParity/service-calls.json \ --exclude-fixture tests/Fixtures/ServerParity/schedule-catalog.json \ + --exclude-fixture tests/Fixtures/ServerParity/namespace-delete-payloads.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 \ @@ -2427,6 +2429,7 @@ jobs: --mode http --url http://parity-rust:8080 --target rust --output /evidence/rust.json \ --exclude-fixture tests/Fixtures/ServerParity/service-calls.json \ --exclude-fixture tests/Fixtures/ServerParity/schedule-catalog.json \ + --exclude-fixture tests/Fixtures/ServerParity/namespace-delete-payloads.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 \ @@ -2440,6 +2443,7 @@ jobs: scripts/conformance/server-parity.mjs compare /evidence/php.json /evidence/rust.json /evidence/embedded.json \ --exclude-fixture tests/Fixtures/ServerParity/service-calls.json \ --exclude-fixture tests/Fixtures/ServerParity/schedule-catalog.json \ + --exclude-fixture tests/Fixtures/ServerParity/namespace-delete-payloads.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 \ @@ -2457,12 +2461,17 @@ jobs: # reference's SQLite maintenance writer even after five retries. schedule_catalog_status=0 bash scripts/conformance/server-parity/update-validation-sqlite.sh http://parity-server:8080 http://parity-rust:8080 schedule-catalog || schedule_catalog_status=$? + # Server #1018: post-commit payload deletion can remain pending + # under the frozen PHP reference's SQLite maintenance writer. + namespace_delete_payloads_status=0 + bash scripts/conformance/server-parity/update-validation-sqlite.sh http://parity-server:8080 http://parity-rust:8080 namespace-delete-payloads || namespace_delete_payloads_status=$? # A native fixture difference fails the step only after the # comparison has run, so one difference cannot hide the rest. test "$native_record_status" -eq 0 test "$compare_status" -eq 0 test "$service_calls_status" -eq 0 test "$schedule_catalog_status" -eq 0 + test "$namespace_delete_payloads_status" -eq 0 - name: Compare published patch, legacy-marker and range-repair profiles if: ${{ matrix.part == 'restarts' }} env: diff --git a/scripts/conformance/server-parity/native-image-corpus.sh b/scripts/conformance/server-parity/native-image-corpus.sh index 405c84acf..4a1a70b8e 100755 --- a/scripts/conformance/server-parity/native-image-corpus.sh +++ b/scripts/conformance/server-parity/native-image-corpus.sh @@ -30,10 +30,11 @@ DW_PARITY_RUNNER_REVISION=$(git rev-parse HEAD) image=$(jq -r '.server.images["linux/amd64"] | sub("^durableworkflow/server@"; "ghcr.io/durable-workflow/server@")' tests/Fixtures/ServerParityBaselines/php-2.11.8.json) # The frozen PHP reference loses SQLite writes under maintenance contention. -# service-calls (#796) and schedule-catalog (#1021) record separately below -# with PHP's loop stopped; native maintenance remains enabled. +# service-calls (#796), schedule-catalog (#1021) and namespace-delete-payloads +# (#1018) record separately with PHP's loop stopped; native maintenance remains enabled. selection=(--exclude-fixture tests/Fixtures/ServerParity/service-calls.json - --exclude-fixture tests/Fixtures/ServerParity/schedule-catalog.json) + --exclude-fixture tests/Fixtures/ServerParity/schedule-catalog.json + --exclude-fixture tests/Fixtures/ServerParity/namespace-delete-payloads.json) for pending in patch-insertion-activity-pending patch-insertion-activity-completed patch-created-activity-pending \ patch-created-activity-completed workflow-waiting-history-type workflow-waiting-history-message workflow-task-transient-retry; do selection+=(--include-fixture "tests/Fixtures/ServerParityPending/$pending.json") @@ -119,15 +120,19 @@ bash scripts/conformance/server-parity/update-validation-sqlite.sh http://parity schedule_catalog_status=0 bash scripts/conformance/server-parity/update-validation-sqlite.sh http://parity-server:8080 http://parity-rust:8080 schedule-catalog \ || schedule_catalog_status=$? +namespace_delete_payloads_status=0 +bash scripts/conformance/server-parity/update-validation-sqlite.sh http://parity-server:8080 http://parity-rust:8080 namespace-delete-payloads \ + || namespace_delete_payloads_status=$? native_id=$(docker image inspect --format '{{.Id}}' "$native") jq -n --arg native "$native" --arg id "$native_id" --arg php "$image" --arg revision "$DW_PARITY_RUNNER_REVISION" \ --argjson reference "$status" --argjson nativeStatus "$native_status" --argjson compare "$compare_status" \ --argjson serviceCalls "$service_calls_status" \ --argjson scheduleCatalog "$schedule_catalog_status" \ + --argjson namespaceDeletePayloads "$namespace_delete_payloads_status" \ '{schema:"durable-workflow.native-image-corpus.v1", native_image:$native, native_image_id:$id, php_reference:$php, runner_revision:$revision, backend:"sqlite", - status:{reference_records:$reference, native_record:$nativeStatus, compare:$compare, service_calls:$serviceCalls, schedule_catalog:$scheduleCatalog}}' \ + status:{reference_records:$reference, native_record:$nativeStatus, compare:$compare, service_calls:$serviceCalls, schedule_catalog:$scheduleCatalog, namespace_delete_payloads:$namespaceDeletePayloads}}' \ > parity-evidence/native-image-corpus.json cat parity-evidence/native-image-corpus.json test "$status" -eq 0 @@ -135,3 +140,4 @@ test "$native_status" -eq 0 test "$compare_status" -eq 0 test "$service_calls_status" -eq 0 test "$schedule_catalog_status" -eq 0 +test "$namespace_delete_payloads_status" -eq 0 diff --git a/scripts/conformance/server-parity/update-validation-sqlite.sh b/scripts/conformance/server-parity/update-validation-sqlite.sh index 9a9e67873..b1e1ae7ae 100644 --- a/scripts/conformance/server-parity/update-validation-sqlite.sh +++ b/scripts/conformance/server-parity/update-validation-sqlite.sh @@ -6,6 +6,8 @@ # optional third argument names another fixture recorded the same way: # service-calls (#796) or schedule-catalog, whose backfill can lose an # occurrence to PHP's concurrent SQLite maintenance writer (#1021). +# namespace-delete-payloads can leave a post-commit deletion pending (#1018). +# These fixtures keep their exact assertions in the same PHP-only window. # # The published PHP record runs with PHP's maintenance loop stopped. On SQLite # the API server and that loop share one database file. Under the loop's writer diff --git a/tests/Unit/NativeImageCorpusTest.mjs b/tests/Unit/NativeImageCorpusTest.mjs index 060703346..932ac30c3 100644 --- a/tests/Unit/NativeImageCorpusTest.mjs +++ b/tests/Unit/NativeImageCorpusTest.mjs @@ -68,10 +68,10 @@ esac`, } } -test('native image qualification records every selected fixture once across the two partitions', () => { +test('native image qualification records every selected fixture once across main and isolated records', () => { const result = run(); assert.equal(result.status, 0, result.stderr); - assert.deepEqual(result.helpers, ['service-calls', 'schedule-catalog']); + assert.deepEqual(result.helpers, ['service-calls', 'schedule-catalog', 'namespace-delete-payloads']); const commands = result.calls.filter(args => args.includes('scripts/conformance/server-parity.mjs')); assert.equal(commands.length, 4, 'three main records and their comparison'); let first; @@ -87,16 +87,19 @@ test('native image qualification records every selected fixture once across the if (first) assert.deepEqual(all, first, 'all record and comparison selections agree'); first = all; } - assert.deepEqual(result.receipt.status, {reference_records: 0, native_record: 0, compare: 0, service_calls: 0, schedule_catalog: 0}); + assert.deepEqual(result.receipt.status, {reference_records: 0, native_record: 0, compare: 0, + service_calls: 0, schedule_catalog: 0, namespace_delete_payloads: 0}); assert.equal(result.receipt.runner_revision, revision); }); for (const [stage, field] of [ ['php', 'reference_records'], ['embedded', 'reference_records'], ['rust', 'native_record'], ['compare', 'compare'], ['service-calls', 'service_calls'], ['schedule-catalog', 'schedule_catalog'], + ['namespace-delete-payloads', 'namespace_delete_payloads'], ]) test(`failure in ${stage} is retained in the receipt and fails image qualification`, () => { const result = run(stage); assert.notEqual(result.status, 0, result.stderr); assert.equal(result.receipt.status[field], 17); - assert.deepEqual(result.helpers, ['service-calls', 'schedule-catalog'], 'one failure does not hide the separate checks'); + assert.deepEqual(result.helpers, ['service-calls', 'schedule-catalog', 'namespace-delete-payloads'], + 'one failure does not hide the separate checks'); }); diff --git a/tests/Unit/ServerParityFixtureSelectionTest.mjs b/tests/Unit/ServerParityFixtureSelectionTest.mjs index 6468ae19b..95d363010 100644 --- a/tests/Unit/ServerParityFixtureSelectionTest.mjs +++ b/tests/Unit/ServerParityFixtureSelectionTest.mjs @@ -57,7 +57,7 @@ function mainCorpusCommands(job) { return job.split(/(?=docker run )/).filter(command => /server-parity\.mjs (record|compare)/.test(command)); } -test('the SQLite job keeps update-validation in its main corpus and isolates service-calls and schedule-catalog', () => { +test('the SQLite job keeps update-validation in its main corpus and isolates the three contention-sensitive fixtures', () => { const sqlite = jobs()['execution-and-codec']; const commands = mainCorpusCommands(sqlite); assert.equal(commands.length, 4, 'php, embedded and rust records plus their comparison'); @@ -65,6 +65,7 @@ test('the SQLite job keeps update-validation in its main corpus and isolates ser assert.deepEqual(command.match(/--exclude-fixture \S+/g), [ '--exclude-fixture tests/Fixtures/ServerParity/service-calls.json', '--exclude-fixture tests/Fixtures/ServerParity/schedule-catalog.json', + '--exclude-fixture tests/Fixtures/ServerParity/namespace-delete-payloads.json', ]); } assert.ok(corpus.includes('update-validation.json')); @@ -73,6 +74,8 @@ test('the SQLite job keeps update-validation in its main corpus and isolates ser assert.match(sqlite, /test "\$service_calls_status" -eq 0/); assert.match(sqlite, /schedule_catalog_status=0\n\s+bash scripts\/conformance\/server-parity\/update-validation-sqlite\.sh http:\/\/parity-server:8080 http:\/\/parity-rust:8080 schedule-catalog \|\| schedule_catalog_status=\$\?/); assert.match(sqlite, /test "\$schedule_catalog_status" -eq 0/); + assert.match(sqlite, /namespace_delete_payloads_status=0\n\s+bash scripts\/conformance\/server-parity\/update-validation-sqlite\.sh http:\/\/parity-server:8080 http:\/\/parity-rust:8080 namespace-delete-payloads \|\| namespace_delete_payloads_status=\$\?/); + assert.match(sqlite, /test "\$namespace_delete_payloads_status" -eq 0/); assert.doesNotMatch(sqlite, /update-validation-sqlite\.sh http:\/\/parity-server:8080 http:\/\/parity-rust:8080 \|\|/); }); diff --git a/tests/Unit/ServerParityUpdateValidationRetryTest.mjs b/tests/Unit/ServerParityUpdateValidationRetryTest.mjs index 1209e7558..3d211ef7a 100644 --- a/tests/Unit/ServerParityUpdateValidationRetryTest.mjs +++ b/tests/Unit/ServerParityUpdateValidationRetryTest.mjs @@ -158,7 +158,7 @@ test('a native or embedded failure is never retried, even after a PHP retry', () } }); -for (const fixture of ['service-calls', 'schedule-catalog']) test(`${fixture} records and retries under its own evidence names`, () => { +for (const fixture of ['service-calls', 'schedule-catalog', 'namespace-delete-payloads']) test(`${fixture} records and retries under its own evidence names`, () => { const result = run('php1=fail-locked', fixture); assert.equal(result.status, 0, result.stderr); assert.deepEqual(result.records, ['php parity-v1', 'php parity-v1-retry', 'embedded parity-v1-retry', 'rust parity-v1-retry']);