Experiment 151: Synchronous Writer Response Resolution

Date: 2026-06-09

Status: Rejected

Direction:stream-rerun-dispatch

Benchmark Run: Tracelite A/B experiment, exp-151-sync-writer-response

Archive: Not created; the candidate was a one-line private completer change,

and the snippet below is enough to recreate it if a future runtime or workload

changes the request-resolution trade-off.

Problem

Experiment 147 left the largest stream-fanout bucket in writer/request

residual rather than SQLite-facing write work. On A11c overlap, SQLite was

9.4% of writer-side burst wall and invalidation was 18.8%, leaving 71.8% as

writer/request residual. Keyed-PK had the same shape, with 63.3% residual.

The updated runner instructions also warned against a standalone residual-split

profiling PR. The bounded question for this run was therefore: can a concrete

request-resolution change reduce that residual bucket without adding another

permanent counter?

Hypothesis

Writer._request resolves responses through a per-request RawReceivePort and

an async Completer<T>(). Switching that completer to Completer<T>.sync()

should remove one response-resolution scheduling hop and make write completion

behave more like the reader completion path, which already uses synchronous

completers for hot reply handling.

This is only worth carrying if the Tracelite stream-dispatch lanes show an

end-to-end measured-elapsed improvement. Reject if the candidate merely moves

profile counters, is neutral, or makes keyed-PK / many-streams work slower.

Approach

The candidate changed only the writer response completer:

 final completer = Completer<T>.sync(); 

No temporary instrumentation was added. A focused correctness run covered

database, transaction, and stream behavior before performance measurement. A

writer-wall profile smoke checked that the candidate touched the intended

request/residual area, then the formal integrated Tracelite A/B wrapper ran

against the stream preset.

Tracelite A/B command:

 dart run benchmark/run_tracelite_experiment.dart \ --dart=/usr/local/bin/dart \ --tracelite-root=/Users/dan/.codex/worktrees/tracelite-pinned-a2bf364 \ --baseline-root=/Users/dan/.codex/worktrees/resqlite-exp151-baseline \ --candidate-root=/Users/dan/.codex/worktrees/resqlite-exp151-stream-scheduling \ --label=exp-151-sync-writer-response \ --direction=stream-rerun-dispatch \ --runs=2 \ --min-repetitions=5 \ --max-repetitions=12 \ --out-dir=build/tracelite-experiments/exp-151-sync-writer-response 

Artifacts:

Results

The wrapper collected clean baseline and candidate histories:

stepstatus
baseline suite historyok
candidate suite historyok
graph data exportvalid
decision artifactinconclusive

Tracelite decision policy:

fieldvalue
expectationimprovement
primary threshold28.5%
max guardrail regression21.5%
max CV21.5%

Decision comparisons:

rolescenariopeermetricbaselinecandidatechangemax CVpstatuseffect
primaryhigh-cardinality-fanoutresqlitemeasured_elapsed_ns380 ms391 ms+2.92%8.20%0.529neutralinconclusive
primarykeyed-pk-subscriptionsresqlitemeasured_elapsed_ns318 ms377 ms+18.5%50.5%0.432too_noisyinconclusive
primarymany-streams-writer-throughputresqlitemeasured_elapsed_ns587 ms669 ms+14.0%17.1%0.000325neutralinconclusive
guardrailkeyed-pk-subscriptionsresqlitemeasured_elapsed_ns318 ms377 ms+18.5%50.5%0.432too_noisyinconclusive

Decision insights:

severityfindingdetail
warningDecision is inconclusiveEvidence is not strong enough for a production decision.
warningGuardrails are inconclusiveOne guardrail comparison needs cleaner or repeated evidence.
warningNoise is the next actionKeyed-PK exceeded the 21.5% CV gate.
warningPrimary metric did not clearHigh-cardinality fanout changed +2.92% with neutral status and CI -16.6 ms..38.8 ms.
warningPrimary metric did not clearKeyed-PK changed +18.5% with too-noisy status and CI -29.9 ms..148 ms.
warningPrimary metric did not clearMany-streams writer throughput changed +14.0% with neutral status and CI -6.06 ms..171 ms.

The profile smoke was also not a clean win. A first current-tree pass looked

materially worse on A11c overlap, and a warmed rerun was mixed: overlap and

disjoint moved favorably, but no-stream writer wall moved the wrong way. The

formal Tracelite result is the decision source.

Decision

Reject.

Synchronous writer response resolution is a coherent request-scheduling

implementation attempt, but it did not clear the stream-dispatch primary gate

and trended slower on the two write-heavy lanes that matter most here:

keyed-PK subscriptions (+18.5%, too noisy) and many-streams writer throughput

(+14.0%, neutral but wrong direction).

The runtime change was removed before opening the PR. This is not a

measurement-only branch: the run consumed a concrete implementation candidate

and used existing Tracelite artifacts to reject it.

Future Notes

unless a Dart runtime change or a new workload changes the scheduling cost

model.

needs a more structural candidate than making the existing response future

synchronous.

opening another residual-split profiling PR, the branch attempted the narrow

request-resolution change, measured it with the integrated stream A/B, then

removed the rejected code.

Validation