Skip to content

delivery: price the byte-cut from honest sampled bytes, not entry nbytes - #81

Merged
jghoman merged 1 commit into
mainfrom
jakob/honest-bytes
Aug 27, 2026
Merged

delivery: price the byte-cut from honest sampled bytes, not entry nbytes#81
jghoman merged 1 commit into
mainfrom
jakob/honest-bytes

Conversation

@jghoman

@jghoman jghoman commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Problem

Production entry tables over-report pa.Table.nbytes by ~150×. Root cause (found during this work — the earlier "zero-copy slices" theory was wrong on current pyarrow): dictionary-encoded columns from the parquet read path. Every chunk of a sliced dictionary column counts the entire shared dictionary — measured 41KB/row against ~300B honest, matching production's implied ~47KB/row — and combine_chunks() does not correct it (unified dictionaries keep unreferenced entries).

On 2026-08-26 the #80 adaptive byte-cut divided by the inflated number and sliced team-2 into ~2,850-row crumb flushes with a frozen AIMD growth gate. flush_adaptive: false has been the fleet-wide stopgap since (codified in charts#14747). The same inflated units are why team-2's 32GiB buffer cap admits only 1-2 entries (the cap-flap sawtooth).

Change

_estimate_row_bytes: sample ≤1,024 head rows, cast to a recursively dictionary-free schema (struct / list / large_list / fixed_size_list / list_view / map all rewritten — nested dictionaries inflate identically), price per-row from the decoded copy. Every consumer that sizes work now shares those units:

  • take-loop admission + within-entry split (rows × per_row);
  • the bytes trigger, via a cached per-destination estimate (QE finding: raw buf.bytes against an honest target fired at ~target/150 of content — trickle flushes that could never satisfy the fill gate, freezing a halved target forever);
  • AIMD evidence, threaded to _flush as est_bytes so the fill gate judges growth in the units the cut used.

Totality: any pyarrow failure degrades to 0 = unpriceable → byte-cut inert, 60K rows-cap backstop (the flag-off behavior) — never a poll-cycle fatal. Sampling falls back past zero-row/unpriceable head entries. One warning per (destination, exception type).

Deliberately unchanged: buffer gauges/caps stay on raw nbytes — prod cap values are tuned against inflated units; re-rating is a coordinated follow-up with charts (the 32GiB comment there already says so).

Review trail

Three adversarial passes: initial review (HOLD: estimator totality, nested dictionaries), verification pass (caught the vacuous AIMD test — replaced with a mutation-verified no-spurious-growth discriminator; found fixed_size_list), independent QE breaker (found the bytes-trigger unit mismatch + zero-row-head disarm; reproductions kept in-tree as tests/unit/test_honest_bytes_qe.py, including quantified sampling-skew design limits). All required findings fixed. 864 unit tests green.

Rollout

Deploy is inert: flush_adaptive is false in prod charts, and with the flag off the estimator is never called (behavior byte-identical to today's stopgap). The follow-up charts PR flips delivery.flushAdaptive: true for prod-us and watches team-2's flush sizes, target evolution, and the cap-flap panel.

Production entry tables over-report pa.Table.nbytes ~150x. Root cause
is dictionary-encoded columns from the parquet read path: every chunk
of a sliced dictionary column counts the entire shared dictionary
(measured 41KB/row against ~300B honest; multi-chunk entries multiply
it per chunk), and combine_chunks() does not correct it — unified
dictionaries keep unreferenced entries. On 2026-08-26 the adaptive
byte-cut divided by the inflated number and sliced team-2 into
~2,850-row flushes with a frozen growth gate; flush_adaptive has been
disabled fleet-wide since.

_estimate_row_bytes samples <=1,024 head rows, casts to a recursively
dictionary-free schema (struct/list/large_list/fixed_size_list/
list_view/map all rewritten — nested dictionaries inflate identically),
and prices per-row from the decoded copy. Everything that sizes work
now shares those units:

- take-loop admission and the within-entry split (rows x per_row);
- the bytes TRIGGER, via a cached per-destination estimate (QE: raw
  buf.bytes against an honest target fired at ~target/150 of real
  content — trickle flushes that could never satisfy the fill gate);
- the AIMD's evidence, threaded to _flush as est_bytes (the fill gate
  must judge growth in the units the cut used, or a learned-down
  target can never grow back).

The estimator is total: any pyarrow failure degrades to 0 =
unpriceable, byte-cut inert, 60K rows cap backstops — never a poll
cycle fatal (the escape handler exits the pod). Sampling falls back
past zero-row/unpriceable head entries. Failures log once per
(destination, exception type).

Buffer gauges and caps deliberately stay on raw nbytes: their prod
values are tuned against inflated units; re-rating them is a separate
coordinated change with charts.

Adversarially reviewed x3 (initial HOLD, verification pass, QE
breaker); all required findings fixed, QE reproductions kept in-tree
(tests/unit/test_honest_bytes_qe.py). 864 unit tests green.

Re-enabling flush_adaptive in charts is the follow-up once this
deploys.
@jghoman
jghoman merged commit e5c1e6f into main Aug 27, 2026
16 checks passed
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.

1 participant