ci: add a manual workflow for the frame-transaction measurement harnesses - #12939
ci: add a manual workflow for the frame-transaction measurement harnesses#12939AnkushinDaniil wants to merge 5 commits into
Conversation
The concurrency group intentionally omits the ref and keeps in-progress runs alive so dispatches queue for the single shared measurement runner instead of cancelling each other.
|
Claude finished @AnkushinDaniil's task in 4m 43s —— View job Review:
|
|
Superseded: #12738 stays on master. The measurement workflow is a manual workflow_dispatch bound to the reproducible-benchmarks self-hosted runner, which lives on the default branch, so it belongs on master rather than the devnet7 feature branch. |
| run_harness Nethermind.Evm.Test FrameTxVerifyDosMeasurement || status=1 | ||
| fi | ||
| if [[ "${HARNESS}" == "both" || "${HARNESS}" == "mempool" ]]; then | ||
| run_harness Nethermind.TxPool.Test FrameTxMempoolDosMeasurement || status=1 |
There was a problem hiding this comment.
Critical — the fixture name does not exist on this branch, so every default dispatch fails.
There is no FrameTxMempoolDosMeasurement anywhere in the tree. The TxPool harness on eip8141-frame-txs-devnet7 is Nethermind.TxPool.Test/FrameTxPrefixRetryMeasurement.cs (there is also Nethermind.Blockchain.Test/FrameTxProducerRetryMeasurement.cs).
So the presence probe on line 56 trips, run_harness returns 1, and — because both is the default input — the out-of-the-box dispatch always ends with ::error::FrameTxMempoolDosMeasurement is not present on <ref> and a red run, even though the verify half succeeded.
| run_harness Nethermind.TxPool.Test FrameTxMempoolDosMeasurement || status=1 | |
| run_harness Nethermind.TxPool.Test FrameTxPrefixRetryMeasurement || status=1 |
The mempool choice label on line 13 stays accurate, but consider whether producer (for FrameTxProducerRetryMeasurement, the third harness) belongs in the option list too — it is the one that measures the per-attempt re-execution count that the other two multiply out.
| echo '```' | ||
| # Only the reported figures, so the summary stays well inside its 1 MiB | ||
| # cap and no stray backtick from test output breaks the fence. | ||
| grep 'RESULT' "${report}" || echo 'no RESULT lines; see the uploaded artifact' |
There was a problem hiding this comment.
High — the harnesses do not put their figures on stdout, so this grep will report no RESULT lines.
FrameTxPrefixRetryMeasurement.Emit (and the identical Emit in FrameTxProducerRetryMeasurement) writes the RESULT … line only to a file:
string path = Environment.GetEnvironmentVariable("FRAME_RETRY_OUT")
?? Path.Combine(Path.GetTempPath(), "frame-prefix-retry.txt");
File.AppendAllText(path, $"RESULT {line}{Environment.NewLine}");and its own <remarks> says why: "because the test runner swallows console writers". FrameTxVerifyDosMeasurement does TestContext.Out.WriteLine(line) and appends to a hard-coded /tmp/frame-dos-results.txt — under the NUnit MTP runner TestContext.Out for a passing test is not echoed to the console either, so the teed ${fixture}.txt is likely to contain no RESULT at all for any harness.
Two consequences:
- The step summary and the uploaded artifact carry no figures — the workflow runs the measurement and then throws the measurement away.
Path.GetTempPath()//tmpon a persistent self-hosted runner plusAppendAllTextmeans the files accumulate across dispatches, so anything read out of them later silently mixes runs.
Fix: point the harness at ${OUTPUT_DIR} and truncate first, e.g. in the run step
env:
HARNESS: ${{ inputs.harness }} local out="${OUTPUT_DIR}/${fixture}-results.txt"
: > "${out}"
FRAME_RETRY_OUT="${out}" dotnet test ...and then grep 'RESULT' "${OUTPUT_DIR}/${fixture}"*.txt. FrameTxVerifyDosMeasurement's /tmp/frame-dos-results.txt is hard-coded, so either copy it into ${OUTPUT_DIR} or teach that harness to honour FRAME_RETRY_OUT like the other two.
| fi | ||
| echo "::group::${fixture}" | ||
| dotnet test --project "${project}/${project}.csproj" -c release -- \ | ||
| --no-ansi --filter "FullyQualifiedName~${fixture}" \ |
There was a problem hiding this comment.
High (needs one local verification run) — both harness fixtures are [Explicit("measurement harness")].
NUnit does not run [Explicit] tests unless the filter counts as an explicit selection, and these projects use the NUnit MTP runner (EnableNUnitRunner=true in src/Nethermind/tests.props), whose filter handling is not the VSTest adapter's. Two ways this can go wrong, and both are invisible until someone dispatches the workflow:
- the runner rejects/ignores VSTest-style
FullyQualifiedName~…syntax, or - it accepts it but still treats the fixture as explicit-only and runs zero tests.
Zero tests ran is at least not silently green (MTP exits non-zero on that), so the failure mode is a red run rather than an empty report — but the workflow as written may never have executed a harness. Please dispatch it once on the branch (or run the exact command locally) and confirm the harness bodies actually execute; if they do not, the filter needs whatever explicit-run form the NUnit MTP runner requires.
I could not verify this myself — dotnet test is not runnable in this review sandbox.
| # Deliberately deviates from the repo default: the group carries no `github.ref` | ||
| # and in-progress runs are not cancelled, so dispatches queue instead of | ||
| # contending for the single shared measurement runner. Do not "fix" this back to | ||
| # the PR-workflow default -- two concurrent runs would distort each other's numbers. | ||
| concurrency: | ||
| group: ${{ github.workflow }} | ||
| cancel-in-progress: false |
There was a problem hiding this comment.
Low — the rationale is right, the word "queue" overstates what GitHub does.
Dropping github.ref from the group and keeping cancel-in-progress: false is the correct call for a single shared measurement runner, and documenting it against the repo default in .agents/rules/github-workflows.md is welcome. But a concurrency group holds one pending run, not a queue: with a run in progress, dispatch #2 waits and dispatch #3 cancels #2. Worth a word in the comment so nobody is surprised when a dispatch disappears.
| run_harness() { | ||
| local project=$1 fixture=$2 rc=0 | ||
| if [[ -z "$(find "${project}" -name "${fixture}.cs" -print -quit)" ]]; then | ||
| echo "::error::${fixture} is not present on ${GITHUB_REF_NAME}. Dispatch this workflow against a ref that carries the harness." | ||
| return 1 | ||
| fi |
There was a problem hiding this comment.
Low — the presence probe assumes file name == fixture name, and find is doing a job [[ -f ]] can do.
It happens to hold for these harnesses, but it couples the workflow to a file-naming convention rather than to the type name; a fixture moved into a differently-named file reports "not present on this ref" while the test is right there. A grep -rq "class ${fixture}\b" "${project}" probe keys off the thing the --filter actually matches, and costs the same.
Minor also: set -uo pipefail without -e means the mkdir -p "${OUTPUT_DIR}" on line 52 can fail unnoticed. set -u does abort the step if OUTPUT_DIR is unset, so the dangerous case is covered — but a mkdir that fails for any other reason (permissions on the shared runner) surfaces only later as a tee error.
Re-bases #12738 onto
eip8141-frame-txs-devnet7where the frame-tx measurement harnesses live. Same workflow file, no content change; the five commits collapse to the single 103-line workflow. Closes #12738 (was based onmaster).