Skip to content

test(acceptance): measure what a destination costs, and whether N cost N times it - #702

Merged
rainmanjam merged 1 commit into
mainfrom
test/concurrency-harness-380
Sep 4, 2026
Merged

test(acceptance): measure what a destination costs, and whether N cost N times it#702
rainmanjam merged 1 commit into
mainfrom
test/concurrency-harness-380

Conversation

@rainmanjam

Copy link
Copy Markdown
Owner

Refs #380 — the concurrency gap. README.md:59 and docs/COMPARISON.md:268
publish "roughly 4% of one core per destination" and nothing tested it. A
reader sizes a box with that number.

It measures through the product, and that matters

One destination, then six, sampling cumulative CPU per child from the process
table — time rather than %cpu, for the reason the ladder driver already
records (%cpu averages over a process's whole lifetime).

  1 destination    asked for 1, alive 1, measured 1, 0.0269 cores total
  6 destinations   asked for 6, alive 6, measured 6, 0.2063 cores total

  cost of one destination:        2.69% of a core
  cost of 6, per destination:     3.44% of a core
  linearity ratio (per-N / per-1): 1.28

The false result it refuses

Measuring this by hand against raw ffmpeg produced:

destinations per destination
1 3.80%
2 2.23%
4 1.11%
8 0.55%

Per-destination cost halving with every doubling — exactly the result anyone
measuring this wants, and a complete artefact. Every run had one surviving
process.
Several ffmpeg readers on one UDP unicast socket compete for packets
and all but one die, so the "total" was one survivor's CPU divided by N. It was
caught only because that harness happened to print a liveness count.

So this suite asserts liveness before cost, everywhere. The product doesn't
have the problem — internal/relay.Hub gives each destination its own
subscription port, which the run above confirms with six alive of six.

It also corrects a number I published

That same standalone harness reported 8.8% of a core, which I posted to #380
as "about double the documented 4%". Measured through the product: 2.69% for
one, 3.44% each for six
. The published ~4% is corroborated, not
contradicted
— the 8.8% was an artefact of measuring something that wasn't a
destination (different args, file output, no relay). Correction is going on the
issue too.

What it asserts is the shape, not a percentage

"4% of a core" is a property of a machine; a suite pinning a number would fail on
hardware rather than on regressions. The three that hold anywhere:

  • every destination asked for is still running at the end
  • every one burned measurable CPU — alive and costing nothing isn't delivering,
    which is why that's a separate check
  • the Nth costs about what the first did, within 0.4×–2.5×

The absolute figure is printed every run, noting that this machine isn't the
six-core VPS the README's number came from.

Validated by watching it fail — which took two goes

First attempt: SIGKILL the destination children. The suite passed a run it
should have failed
supervisor.Spec carries AutoRestart, so they came
back before the sample. Stopping them through the API instead retires them,
and then it fails correctly:

  FAIL  asked for 6 destinations, 0 were alive at the end
        This is the failure the suite exists to catch...
  FAIL  per-destination cost at 6 is 0.000x the cost at 1, outside 0.4-2.5
        BELOW the band, which is the direction the false result went...

A guard nobody has watched fail is a guard nobody should trust.

Not wired into CI here. It spawns seven ffmpeg processes and measures for a
minute; where it belongs in the matrix is a separate decision from whether it
works.

https://claude.ai/code/session_01A8N3W5ct9SZtHK9sCDD9cL

…t N times it

#380's concurrency gap. README.md:59 and docs/COMPARISON.md:268 both publish
"roughly 4% of one core per destination" and nothing tested it -- a reader sizes
a box with that number.

WHAT IT MEASURES, through the real server rather than raw ffmpeg: one
destination, then six, sampling cumulative CPU per child out of the process
table. `time` rather than `%cpu`, for the reason the ladder driver already
records -- %cpu averages over a process's whole lifetime, so a child up for a
minute barely moves it.

THE FALSE RESULT IT REFUSES, which is why liveness is asserted before cost
everywhere. Measuring this by hand against raw ffmpeg produced:

    1 destination:  3.80% of a core
    2 destinations: 2.23% each
    4 destinations: 1.11% each
    8 destinations: 0.55% each

Per-destination cost halving with every doubling: exactly the result anyone
measuring this wants, and an artefact. Every run had ONE surviving process --
several ffmpeg readers on one UDP unicast socket compete for packets and all but
one die, so the total was one survivor's CPU divided by N. It was caught only
because that harness happened to print a liveness count. The product does not
have the problem: internal/relay.Hub gives each destination its own subscription
port, which this run confirms with six alive out of six.

AND THE FIGURE IT CORRECTS IS MY OWN. That standalone harness also reported 8.8%
of a core for one destination, which I posted to #380 as "about double the
documented 4%". Measured through the product instead: 2.69% for one, 3.44% each
for six. The published ~4% is corroborated, not contradicted, and the 8.8% was
an artefact of measuring something that was not a destination -- different args,
file output, no relay. The correction is going on the issue too.

WHAT IT ASSERTS IS THE SHAPE, NOT A PERCENTAGE. "4% of a core" is a property of
a machine; a suite pinning a number would fail on hardware rather than on
regressions. The three that hold anywhere:

  - every destination asked for is still running at the end
  - every one burned measurable CPU (alive and costing nothing is not delivering,
    which is why that is a separate check)
  - the Nth costs about what the first did, within 0.4x-2.5x

The absolute figure is PRINTED every run, with the note that this machine is not
the six-core VPS the README's number came from, so a difference is not by itself
a defect in either.

VALIDATED BY WATCHING IT FAIL, and the first attempt to do that did not work.
SIGKILLing the destination children was not enough: supervisor.Spec carries
AutoRestart, so they came back before the sample and the suite passed a run it
should have failed. Stopping them through the API instead RETIRES them, and then
it fails correctly -- naming the count, and printing the "below the band" branch
that says work which appears to vanish as load grows usually has not been done.
A guard nobody has watched fail is a guard nobody should trust, and this one
needed two goes.

Not wired into CI in this change. It spawns seven ffmpeg processes and measures
for a minute; where it belongs in the matrix is a separate decision from whether
it works.

Refs #380.

Claude-Session: https://claude.ai/code/session_01A8N3W5ct9SZtHK9sCDD9cL
Copilot AI lite review requested due to automatic review settings September 4, 2026 19:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new driver/harness can mis-measure or fail to surface driver errors due to facts.env quoting and incomplete validation of the measured PID set across the sampling window.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a new acceptance harness to measure per-destination CPU cost and validate that cost scales roughly linearly with destination count, closing the untested “~4% of a core per destination” concurrency claim referenced in #380.

Changes:

  • Introduces scripts/acceptance-concurrency.sh to run the product, drive destination creation via the API, and assert liveness/work/linearity shape.
  • Adds a Go “driver” (scripts/acceptance_concurrency_driver.go, run via go run) that creates destinations, samples per-process cumulative CPU from ps, and writes a facts.env file for the shell harness.
File summaries
File Description
scripts/acceptance-concurrency.sh New shell entrypoint that boots the server, runs the driver, and performs assertions (liveness, non-zero work, linearity band).
scripts/acceptance_concurrency_driver.go New Go driver that provisions destinations and measures CPU consumption via process-table sampling, emitting results to facts.env.
Review details

Suppressed comments (1)

scripts/acceptance_concurrency_driver.go:216

  • The test’s safety property depends on measuring only destinations that were present for the entire window; if a destination dies/restarts mid-window (new PID), alive may still equal want but the PID intersection will shrink and CPU will be understated. Treat a PID-set change during the window as a hard failure so the suite can’t produce the known-false "too good" linearity result.
	var pids []string
	for pid := range after {
		if _, ok := before[pid]; ok {
			pids = append(pids, pid)
		}
	}
  • Files reviewed: 2/2 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

# able to read it off a CI log.
#
# Usage: ./scripts/acceptance-concurrency.sh [workdir]
set -u
Comment on lines +194 to +199
before := destCPU()
if len(before) < want {
fmt.Printf(" %-16s FAILED to start: %d of %d destination children exist\n",
label, len(before), want)
}
const window = 20 * time.Second
Comment on lines +326 to +328
for _, k := range keys {
fmt.Fprintf(&b, "%s=%s\n", k, facts[k])
}
@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

@rainmanjam
rainmanjam merged commit 9de456a into main Sep 4, 2026
36 checks passed
@rainmanjam
rainmanjam deleted the test/concurrency-harness-380 branch September 4, 2026 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants