From 7b82310137a561960bca869eebca3ec51101541f Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:50:53 +0000 Subject: [PATCH 1/2] Split MariaDB native execution tests across the existing two shards --- .github/workflows/server-parity.yml | 15 ++++++++++----- tests/Unit/ServerParityPartsTest.mjs | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/server-parity.yml b/.github/workflows/server-parity.yml index 36bc297d0..afe5e2b4d 100644 --- a/.github/workflows/server-parity.yml +++ b/.github/workflows/server-parity.yml @@ -484,8 +484,8 @@ jobs: # Each database runs in six parts on separate runners: the native # database suites and TLS checks, the shared corpus, its profiles and # restart cases, the published-SDK direction profiles, the corrected - # marker cohort, and the other cohorts. The two MySQL 8 labels add a - # seventh, native-tests-b, for half their execution tests. Every part + # marker cohort, and the other cohorts. Each label adds a seventh, + # native-tests-b, for half its execution tests. Every part # bootstraps the same database and native build; no part depends on # another. matrix: @@ -496,9 +496,8 @@ jobs: image: mirror.gcr.io/library/mysql@sha256:62fb722c78b24245ddff1796a0fcee4a49cc5b87e0aaaf20c92d1da9e0a2497b # The expanded original was OOM-killed at 1 GiB during native pressure. memory: 2g - # The two MySQL 8 labels' native suites were the slowest parts - # (about 21 min each, most of it the execution tests), so their - # execution tests run in two halves: native-tests runs the first + # The native execution suites run in two halves to keep one + # database label from holding the result: native-tests runs the first # and native-tests-b the second. The bytewise-sorted test list is # dealt round-robin, so each listed test runs in exactly one half # and a new test joins one. @@ -522,6 +521,12 @@ jobs: image: mirror.gcr.io/library/mariadb@sha256:7db29378d4fdab73f8123bbc2b48905c90d1a4b00cf848b028f1e81e623257f2 # Both expanded originals exhausted the 512 MiB DB fixture ceiling. memory: 1g + execution_shards: 2 + - label: mariadb-10.11 + part: native-tests-b + image: mirror.gcr.io/library/mariadb@sha256:7db29378d4fdab73f8123bbc2b48905c90d1a4b00cf848b028f1e81e623257f2 + memory: 1g + execution_shards: 2 env: DW_PARITY_PART: ${{ matrix.part }} DW_EXECUTION_SHARDS: ${{ matrix.execution_shards || 1 }} diff --git a/tests/Unit/ServerParityPartsTest.mjs b/tests/Unit/ServerParityPartsTest.mjs index e9d3a7781..e53c37545 100644 --- a/tests/Unit/ServerParityPartsTest.mjs +++ b/tests/Unit/ServerParityPartsTest.mjs @@ -144,7 +144,7 @@ test('the database bootstraps run the native suites only in the native-tests par assert.match(mysql.text, /'case "\$DW_PARITY_PART" in native-tests\) shard=1 suffix= ;; native-tests-b\) shard=2 suffix=-b ;; \*\) shard= ;; esac; if \[ "\$DW_PARITY_PART" = native-tests \]; then cargo test [^']*runtime::mysql::[^']* > \/evidence\/mysql-tests\.txt \|\| exit 1; fi; if \[ -n "\$shard" \]; then [^']*cargo test --locked --offline --test execution -- --exact --nocapture --test-threads=2 \$\(cat \/evidence\/execution-shard\$suffix\.txt\) > \/evidence\/mysql-execution-tests\$suffix\.txt \|\| exit 1; fi; case "\$DW_PARITY_PART" in native-tests\|native-tests-b\) cargo build --locked --offline ;; \*\) test -x \/target\/debug\/durable-workflow-server ;; esac'/); }); -test('the MySQL 8 labels split their execution tests into two parts, every test in exactly one', () => { +test('all MySQL and MariaDB labels split execution tests into two parts, every test in exactly one', () => { const block = job('mysql-execution'); assert.match(block, /^ {6}DW_EXECUTION_SHARDS: \$\{\{ matrix\.execution_shards \|\| 1 \}\}$/m); // Matrix values come from the label's include entry; the added @@ -152,7 +152,7 @@ test('the MySQL 8 labels split their execution tests into two parts, every test const include = block.slice(block.indexOf('\n include:\n'), block.indexOf('\n env:\n')); const entries = include.split(/\n {10}- /).slice(1).map(text => Object.fromEntries( [...('label: ' + text.replace(/^label: /, '')).matchAll(/^\s*([a-z_]+): (\S+)$/gm)].map(match => match.slice(1)))); - const sharded = ['mysql-8.0', 'existing-php-mysql-matrix']; + const sharded = ['mysql-8.0', 'existing-php-mysql-matrix', 'mariadb-10.11']; for (const name of sharded) { const pair = entries.filter(entry => entry.label === name); assert.equal(pair.length, 2, JSON.stringify(entries)); From 66a2b0bfc7c21b696020feb3c4d27897892bef61 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Thu, 5 Nov 2026 20:04:08 +0000 Subject: [PATCH 2/2] Retain bounded execution selections and refuse failed listings --- .github/workflows/server-parity.yml | 2 +- .../server-parity/artifact-summary.mjs | 1 + tests/Unit/NativeDebugArtifactTest.mjs | 23 +++++++++++++++---- .../Unit/ServerParityArtifactSummaryTest.mjs | 19 +++++++++++++++ tests/Unit/ServerParityPartsTest.mjs | 4 ++-- 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/.github/workflows/server-parity.yml b/.github/workflows/server-parity.yml index afe5e2b4d..f3473f0b3 100644 --- a/.github/workflows/server-parity.yml +++ b/.github/workflows/server-parity.yml @@ -712,7 +712,7 @@ jobs: -v "$PWD:/source:ro" -v "$PWD/cargo-home:/cargo" -v "$PWD/rust-target:/target" \ -v "$PWD/parity-evidence:/evidence" -w /source/rust --entrypoint bash \ "${NATIVE_RUST_IMAGE}" -c \ - 'case "$DW_PARITY_PART" in native-tests) shard=1 suffix= ;; native-tests-b) shard=2 suffix=-b ;; *) shard= ;; esac; if [ "$DW_PARITY_PART" = native-tests ]; then cargo test --locked --offline --lib runtime::mysql:: -- --ignored --skip runtime::mysql::tests::bootstrap_child --nocapture --test-threads=2 > /evidence/mysql-tests.txt || exit 1; fi; if [ -n "$shard" ]; then cargo test --locked --offline --test execution -- --list --format terse | sed -n "s/: test\$//p" | LC_ALL=C sort | awk -v count="$DW_EXECUTION_SHARDS" -v shard="$shard" "(NR - 1) % count == shard - 1" > /evidence/execution-shard$suffix.txt && test -s /evidence/execution-shard$suffix.txt && cargo test --locked --offline --test execution -- --exact --nocapture --test-threads=2 $(cat /evidence/execution-shard$suffix.txt) > /evidence/mysql-execution-tests$suffix.txt || exit 1; fi; case "$DW_PARITY_PART" in native-tests|native-tests-b) cargo build --locked --offline ;; *) test -x /target/debug/durable-workflow-server ;; esac' + 'case "$DW_PARITY_PART" in native-tests) shard=1 suffix= ;; native-tests-b) shard=2 suffix=-b ;; *) shard= ;; esac; set -o pipefail; if [ "$DW_PARITY_PART" = native-tests ]; then cargo test --locked --offline --lib runtime::mysql:: -- --ignored --skip runtime::mysql::tests::bootstrap_child --nocapture --test-threads=2 > /evidence/mysql-tests.txt || exit 1; fi; if [ -n "$shard" ]; then cargo test --locked --offline --test execution -- --list --format terse | sed -n "s/: test\$//p" | LC_ALL=C sort | tee /evidence/execution-listed-tests.txt | awk -v count="$DW_EXECUTION_SHARDS" -v shard="$shard" "(NR - 1) % count == shard - 1" > /evidence/execution-shard$suffix.txt && test -s /evidence/execution-shard$suffix.txt && cargo test --locked --offline --test execution -- --exact --nocapture --test-threads=2 $(cat /evidence/execution-shard$suffix.txt) > /evidence/mysql-execution-tests$suffix.txt || exit 1; fi; case "$DW_PARITY_PART" in native-tests|native-tests-b) cargo build --locked --offline ;; *) test -x /target/debug/durable-workflow-server ;; esac' docker run --rm --user="$(id -u):$(id -g)" --network "$RESOURCE_SCOPE" \ -e DW_RUST_EXPERIMENTAL=1 -e DB_CONNECTION=mysql -e DB_HOST=parity-mysql -e DB_DATABASE=native_ref \ -e DB_USERNAME=root -e DB_PASSWORD=parity-disposable-password \ diff --git a/scripts/conformance/server-parity/artifact-summary.mjs b/scripts/conformance/server-parity/artifact-summary.mjs index 424392a1b..f780fa838 100644 --- a/scripts/conformance/server-parity/artifact-summary.mjs +++ b/scripts/conformance/server-parity/artifact-summary.mjs @@ -18,6 +18,7 @@ function priority(path, failed) { if (failed && (/\.stderr$/.test(name) || /(?:stderr|error|failure|bootstrap|install|build).*(?:\.log|\.txt)$/.test(name) || /^(?:php|rust|native)(?:[-.].*)?\.log$/.test(name) || /server\.log$/.test(name))) return 2; if (/^(identity|build|database-state|database-memory-limit)\.json$/.test(name)) return 3; + if (/^execution-(listed-tests|shard(?:-b)?)\.txt$/.test(name)) return 4; if (/(?:-status|-integrity|-version|-revision|-controls|-contract)\.txt$/.test(name) || /\.status$/.test(name) || /^(SHA256SUMS|source-hashes\.sha256)$/.test(name)) return 4; return null; diff --git a/tests/Unit/NativeDebugArtifactTest.mjs b/tests/Unit/NativeDebugArtifactTest.mjs index e081cba4c..3f134ab71 100644 --- a/tests/Unit/NativeDebugArtifactTest.mjs +++ b/tests/Unit/NativeDebugArtifactTest.mjs @@ -279,7 +279,7 @@ function databaseCommand(id) { return line.trim().slice(1, -1); } -function runDatabaseCommand(t, id, part, binary = true) { +function runDatabaseCommand(t, id, part, binary = true, listExit = 0) { const root = mkdtempSync(join(tmpdir(), 'native-debug-cargo-')); t.after(() => rmSync(root, {recursive: true, force: true})); mkdirSync(join(root, 'bin')); @@ -292,13 +292,20 @@ function runDatabaseCommand(t, id, part, binary = true) { const fs = require('node:fs'); const args = process.argv.slice(2); fs.appendFileSync(process.env.CARGO_CALL_LOG, JSON.stringify(args) + '\\n'); -if(args.includes('--list')) console.log('case_a: test\\ncase_b: test'); +if(args.includes('--list')) { + console.log('case_a: test\\ncase_b: test'); + process.exit(Number(process.env.CARGO_LIST_EXIT || 0)); +} `, {mode: 0o755}); const script = databaseCommand(id).replaceAll('/evidence/', `${root}/evidence/`).replaceAll('/target/', `${root}/target/`); const run = spawnSync('bash', ['-c', script], {encoding: 'utf8', env: {...process.env, - PATH: `${root}/bin:${process.env.PATH}`, CARGO_CALL_LOG: log, DW_PARITY_PART: part, DW_EXECUTION_SHARDS: '2'}}); + PATH: `${root}/bin:${process.env.PATH}`, CARGO_CALL_LOG: log, CARGO_LIST_EXIT: String(listExit), + DW_PARITY_PART: part, DW_EXECUTION_SHARDS: '2'}}); const calls = readFileSync(log, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse); - return {...run, calls}; + const listed = join(root, 'evidence/execution-listed-tests.txt'); + const selected = join(root, `evidence/execution-shard${part === 'native-tests-b' ? '-b' : ''}.txt`); + return {...run, calls, listed: existsSync(listed) ? readFileSync(listed, 'utf8') : null, + selected: existsSync(selected) ? readFileSync(selected, 'utf8') : null}; } for (const part of ['native-tests', 'native-tests-b']) { @@ -314,6 +321,14 @@ for (const part of ['native-tests', 'native-tests-b']) { ['test', ...flags, '--test', 'execution', '--', '--exact', '--nocapture', '--test-threads=2', part === 'native-tests' ? 'case_a' : 'case_b'], ['build', ...flags]); assert.deepEqual(run.calls, expected); + assert.equal(run.listed, 'case_a\ncase_b\n'); + assert.equal(run.selected, part === 'native-tests' ? 'case_a\n' : 'case_b\n'); + }); + test(`MySQL ${part} refuses a failing test-list command even with partial output`, t => { + const run = runDatabaseCommand(t, 'mysql-execution', part, true, 7); + assert.notEqual(run.status, 0); + assert.equal(run.calls.some(args => args.includes('--exact')), false); + assert.equal(run.calls.some(args => args[0] === 'build'), false); }); } diff --git a/tests/Unit/ServerParityArtifactSummaryTest.mjs b/tests/Unit/ServerParityArtifactSummaryTest.mjs index dce60536a..30b7140d9 100644 --- a/tests/Unit/ServerParityArtifactSummaryTest.mjs +++ b/tests/Unit/ServerParityArtifactSummaryTest.mjs @@ -40,6 +40,25 @@ test('successful summaries preserve exact reports and omit journals, DBs, payloa assert.throws(() => summarizeEvidence(source, output), /fresh empty directory/); }); +test('execution inventories retain exact bytes with explicit truncation and omit unrelated lists', t => { + const {source, output} = fixture(t); + const inventories = {'execution-listed-tests.txt': 'case_a\ncase_b\n', + 'execution-shard.txt': 'case_a\n', 'execution-shard-b.txt': 'case_b\n'}; + for (const [name, content] of Object.entries(inventories)) writeFileSync(join(source, name), content); + writeFileSync(join(source, 'execution-unrelated.txt'), 'not a selected-test inventory'); + const summary = summarizeEvidence(source, output, {outcome: 'success'}); + for (const [name, content] of Object.entries(inventories)) { + assert.equal(readFileSync(join(output, name), 'utf8'), content); + assert.equal(summary.retained.find(entry => entry.path === name).tail_only, false); + } + assert.equal(existsSync(join(output, 'execution-unrelated.txt')), false); + const other = fixture(t); + writeFileSync(join(other.source, 'execution-listed-tests.txt'), 'x'.repeat(64 * 1024 + 1)); + const truncated = summarizeEvidence(other.source, other.output, {outcome: 'success'}).retained[0]; + assert.equal(truncated.retained_bytes, 64 * 1024); + assert.equal(truncated.tail_only, true); +}); + test('failed summaries keep bounded diagnostic tails without truncating JSON into a false report', t => { const {source, output} = fixture(t); const diagnostic = `${'x'.repeat(128 * 1024)}\nHTTP 500 while installing pinned dependency\n`; diff --git a/tests/Unit/ServerParityPartsTest.mjs b/tests/Unit/ServerParityPartsTest.mjs index e53c37545..4685aaa79 100644 --- a/tests/Unit/ServerParityPartsTest.mjs +++ b/tests/Unit/ServerParityPartsTest.mjs @@ -141,7 +141,7 @@ test('the database bootstraps run the native suites only in the native-tests par assert.match(mysql.text, /-e DW_PARITY_PART -e DW_EXECUTION_SHARDS \\\n/); // native-tests runs the database suite and the first half of the execution // tests; native-tests-b, where a label has it, runs the second half. - assert.match(mysql.text, /'case "\$DW_PARITY_PART" in native-tests\) shard=1 suffix= ;; native-tests-b\) shard=2 suffix=-b ;; \*\) shard= ;; esac; if \[ "\$DW_PARITY_PART" = native-tests \]; then cargo test [^']*runtime::mysql::[^']* > \/evidence\/mysql-tests\.txt \|\| exit 1; fi; if \[ -n "\$shard" \]; then [^']*cargo test --locked --offline --test execution -- --exact --nocapture --test-threads=2 \$\(cat \/evidence\/execution-shard\$suffix\.txt\) > \/evidence\/mysql-execution-tests\$suffix\.txt \|\| exit 1; fi; case "\$DW_PARITY_PART" in native-tests\|native-tests-b\) cargo build --locked --offline ;; \*\) test -x \/target\/debug\/durable-workflow-server ;; esac'/); + assert.match(mysql.text, /'case "\$DW_PARITY_PART" in native-tests\) shard=1 suffix= ;; native-tests-b\) shard=2 suffix=-b ;; \*\) shard= ;; esac; set -o pipefail; if \[ "\$DW_PARITY_PART" = native-tests \]; then cargo test [^']*runtime::mysql::[^']* > \/evidence\/mysql-tests\.txt \|\| exit 1; fi; if \[ -n "\$shard" \]; then [^']*cargo test --locked --offline --test execution -- --exact --nocapture --test-threads=2 \$\(cat \/evidence\/execution-shard\$suffix\.txt\) > \/evidence\/mysql-execution-tests\$suffix\.txt \|\| exit 1; fi; case "\$DW_PARITY_PART" in native-tests\|native-tests-b\) cargo build --locked --offline ;; \*\) test -x \/target\/debug\/durable-workflow-server ;; esac'/); }); test('all MySQL and MariaDB labels split execution tests into two parts, every test in exactly one', () => { @@ -164,7 +164,7 @@ test('all MySQL and MariaDB labels split execution tests into two parts, every t assert.ok(entries.filter(entry => !sharded.includes(entry.label)).every(entry => !entry.execution_shards && !entry.part)); // The selection the bootstrap runs: list, sort bytewise, deal round-robin. const bootstrap = steps(block).find(step => step.name.startsWith('Bootstrap published ')).text; - const selection = /cargo test --locked --offline --test execution -- --list --format terse \| sed -n "s\/: test\\\$\/\/p" \| LC_ALL=C sort \| awk -v count="\$DW_EXECUTION_SHARDS" -v shard="\$shard" "([^"]+)" > \/evidence\/execution-shard\$suffix\.txt && test -s \/evidence\/execution-shard\$suffix\.txt && /.exec(bootstrap); + const selection = /cargo test --locked --offline --test execution -- --list --format terse \| sed -n "s\/: test\\\$\/\/p" \| LC_ALL=C sort \| tee \/evidence\/execution-listed-tests\.txt \| awk -v count="\$DW_EXECUTION_SHARDS" -v shard="\$shard" "([^"]+)" > \/evidence\/execution-shard\$suffix\.txt && test -s \/evidence\/execution-shard\$suffix\.txt && /.exec(bootstrap); assert.ok(selection, 'the execution tests are selected by the listed round-robin'); const names = Array.from({length: 489}, (_, index) => `suite::test_${String(index).padStart(3, '0')}`); const deal = (shard, count) => {