Skip to content

Integer-proxy chunks: backend-agnostic join and reduce over (key_hash, value_id)#781

Draft
frankmcsherry wants to merge 1 commit into
TimelyDataflow:master-nextfrom
frankmcsherry:int-proxy
Draft

Integer-proxy chunks: backend-agnostic join and reduce over (key_hash, value_id)#781
frankmcsherry wants to merge 1 commit into
TimelyDataflow:master-nextfrom
frankmcsherry:int-proxy

Conversation

@frankmcsherry

Copy link
Copy Markdown
Member

A boundary where only integers cross: a storage backend presents each record as ((key_hash, value_id), time, diff) — integer proxies for data it keeps in its own layout — the operators own all the lattice/time logic over those integers, and the backend supplies value semantics via callbacks. Any columnar (or otherwise opaque-to-DD) value store can then reuse join and reduce without materializing values as Rust types.

The two integers carry different contracts. key_hash is a content hash of the key, stable across the whole system with no registry: the same key hashes identically in every operator, including across the output→input boundary (a reduce output re-ingested downstream). value_id is an intra-key identifier, consistent within one operator computation — equal ids mean equal values there, which is all that consolidation and presence need. Output ids are minted by hashing the produced value, which is what lets a reduce output become a real arrangement whose values present with the same ids downstream. Hash collisions are an accepted risk (birthday bound; the module doc quantifies it, and the upgrade path is a wider id, never a registry).

Contents:

  • trace/chunk/int_proxy: ProxyChunk, a cursor-less Chunk of proxy columns (slots into the Chunk navigation capability #778 split; batches, fueled merging, and grading come free, entirely in integer space), plus from_unsorted — integer sort+consolidate with representative provenance — as the helper a backend uses to build presentations.
  • operators/int_proxy: ProxyJoinTactic / ProxyReduceTactic for the join_with_tactic and reduce_with_tactic seams (made pub here — these tactics are the first out-of-crate-shaped consumers), and the backend traits: present-as-proxies (read), value callback with hash-minted output ids (write), materialize (egress). The reduce tactic keeps its cross-retire pending keyed by the stable key_hash, and restricts presentations to changed keys so incremental cost tracks the delta, not the accumulation. The module doc carries the design notes, including why value_id is deliberately not order-preserving (min/max belong to the value callback).
  • operators/int_proxy/reference: an in-memory backend over VecChunk arrangements with fnv content hashes, so the framework is exercised without any columnar engine.
  • Tests: an equi-join and count/distinct/min reduces checked against the row-based operators over multi-round inputs with retractions, and a scripted Product-time retire sequence exercising synthetic time corrections both within an interval and pended across retires.

🤖 Generated with Claude Code

frankmcsherry added a commit to frankmcsherry/differential-dataflow that referenced this pull request Jul 3, 2026
…flow#781) consumption plan

Branch rebased onto int-proxy: the framework the SPIKE's boundary model
called for now exists upstream (renamed ids -> int_proxy in review);
the old in-branch copy is dropped. The hand-off section documents the
current interfaces, the one-crossing-per-retire reduce_many contract,
the assessment harness to replicate, the reduce-first plan, the traps
already hit once (scan-presents, capacity leaks, giant-chunk settle),
and the suitability questions the corgi agent should answer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
frankmcsherry added a commit to frankmcsherry/differential-dataflow that referenced this pull request Jul 4, 2026
…C harness

Swap the corgi path-dep (../../wip/corgi) for a git-dep pinned to
frankmcsherry/WIP@9b41cdc (dd-arrange-api), so the branch builds standalone
without a local wip checkout. Add int-proxy-columnar-findings.md (the field
report tying TimelyDataflow#781 + this branch + the corgi kernel crate, with the SCC numbers
and the columnar-Time gap) and the large-N SCC / vec-profiling harnesses the
report's repro section references.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
frankmcsherry added a commit to frankmcsherry/differential-dataflow that referenced this pull request Jul 5, 2026
Remove the two superseded modules (corgi_arrange.rs pre-Chunk arrangement,
corgi_reduce.rs pre-int_proxy reduce) — nothing in the live path referenced
them — plus the ~150-line dead stratum inside corgi_chunk.rs that only they
used (from_rows/to_rows/batch_to_rows, rows_to_batch, CorgiChunkBuilder,
group_offsets, semijoin_history) and the orphaned imports.

Also delete the examples that only exercised the dead modules (corgi_arrange_smoke,
corgi_join_mechanism/dataflow, corgi_reduce_dataflow/trace) and the SCC
bug-isolation harnesses (corgi_scc_min/trace/stream, corgi_cc_min, corgi_bwd_min)
for the interesting-times bug now fixed and pinned by tests + Lean.

Live path unchanged: build clean (no warnings), corgi_chunk unit tests pass,
corgi_progs matches vec. Net -2157 lines. Git history preserves the journey.

Also correct int-proxy-columnar-findings.md: the DD subtree is NOT byte-identical
to TimelyDataflow#781 — ~148 lines (MSD-bucket sort, reduce buffer-reuse, tests) sit ahead of
the PR and should be pushed back to int-proxy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@frankmcsherry frankmcsherry marked this pull request as draft July 5, 2026 22:26
frankmcsherry added a commit to frankmcsherry/differential-dataflow that referenced this pull request Jul 6, 2026
G3 (done, stays in TimelyDataflow#781): annotate the proxy reduce tactic's deliberate divergences from
`history_replay::compute`, all cited by symbol name so they don't rot. Added the two that
were missing — the phase-A/B split at the single `reduce_many` crossing (with its S7
justification), and phase B as the compensating output-accumulation the reference does
inline. The others (double-count avoidance, output-times-as-join-base / G1, seeds) were
already inline.

G2 (scoped out): documenting and asserting the `ReduceTactic`/`JoinTactic` contracts —
including the `debug_assert!`s in the shared drivers `reduce_with_tactic`/`join_with_tactic`
— hardens the tactic tier used by the cursor and reference tactics today, independent of
the proxy. So it becomes its own master-next PR that TimelyDataflow#781 conforms to, not a TimelyDataflow#781 gate.
DESIGN.md records the move and the enumerated invariants by enforcement class.

Comments/docs only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
frankmcsherry added a commit to frankmcsherry/differential-dataflow that referenced this pull request Jul 6, 2026
G3 (done, stays in TimelyDataflow#781): annotate the proxy reduce tactic's deliberate divergences from
`history_replay::compute`, all cited by symbol name so they don't rot. Added the two that
were missing — the phase-A/B split at the single `reduce_many` crossing (with its S7
justification), and phase B as the compensating output-accumulation the reference does
inline. The others (double-count avoidance, output-times-as-join-base / G1, seeds) were
already inline.

G2 (scoped out): documenting and asserting the `ReduceTactic`/`JoinTactic` contracts —
including the `debug_assert!`s in the shared drivers `reduce_with_tactic`/`join_with_tactic`
— hardens the tactic tier used by the cursor and reference tactics today, independent of
the proxy. So it becomes its own master-next PR that TimelyDataflow#781 conforms to, not a TimelyDataflow#781 gate.
DESIGN.md records the move and the enumerated invariants by enforcement class.

Comments/docs only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
frankmcsherry added a commit to frankmcsherry/differential-dataflow that referenced this pull request Jul 6, 2026
`key_hash` is a content hash, so it is an independence marker, not identity — colliding keys
share a cell. The framework stays collision-oblivious (sound, just coarse), but exactness is
recoverable *backend-side*, because the backend always holds the real keys and values. Add
that recipe to `mod.rs`'s collision-risk docs:

- Join: verify real keys at `cross` and drop collision-fabricated pairs (their times/diffs
  are never emitted, so the output is exactly the real-key equijoin).
- Reduce: partition the bracket by real key and mint key-qualified value ids
  (`hash(key, value)` or per-`(key, value)` ordinals) so `(key_hash, vid)` tracks
  `(key, value)` exactly.

The unrecoverable residue is granularity only (colliding keys share `pending` and the
changed-key restriction) — extra work, never wrong answers. Also document, on `vec_backend`
itself, that the reference deliberately does NOT implement this — it hashes keys/values and
accepts the birthday bound for simplicity.

Also folds in pending DESIGN.md updates: G2 recorded as done/merged (TimelyDataflow#789) with TimelyDataflow#781 rebased
and conforming; the S1-recipe task marked done. Docs only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
frankmcsherry added a commit to frankmcsherry/differential-dataflow that referenced this pull request Jul 7, 2026
Doc/tracking fixes from review; no behavior change, 14 tests green.
- seed_times: state the sorted-by-key_hash requirement in the trait doc (it was
  only enforced by the tactic's debug_assert!, invisible to an out-of-crate impl).
- mod.rs typo: "difference hashes" -> "different hashes".
- DESIGN.md F8 (new): join fuel/yielding — ProxyJoinTactic::work drains eagerly
  (fuel=0), the one known gap that lived nowhere in the burndown. A refinement.
- DESIGN.md F4: the kit should add a join analog of the reduce's compaction-
  cancellation regression (advancing a fresh presentation to empty) — sound but
  untested, the sound-looking-cancellation shape that produced the TimelyDataflow#781 drift.
- DESIGN.md F5: note join_key is likewise a second interesting-times copy (of the
  cursor JoinThinker); the fuzz/model net covers only reduce today.
- DESIGN.md F7: record that system-wide key_hash stability is load-bearing only
  under hash-native shared arrangements (a deliberate commitment, not a current
  requirement), so S1's recovery-cost story weighs against hash-native storage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
frankmcsherry added a commit to frankmcsherry/differential-dataflow that referenced this pull request Jul 7, 2026
A corgi re-port atop TimelyDataflow#781 hit a real gap: the present_* methods' bridge output
MUST be sorted and consolidated by ((key_hash, value_id), time) — the tactics
merge-walk it — but the docs said only "may consolidate" (optional-sounding) and,
unlike seed_times, nothing asserted it. vec_backend sorts internally so never hit
it; a second, out-of-repo backend exposed it (exactly what S5 exists to catch).

- mod.rs: debug_assert_sorted_bridge (mirrors the seed_times assert); the
  ProxyBridge alias doc now states the sorted+consolidated requirement.
- reduce/join: call it after each present_input/present_output/present0/present1;
  the trait docs now say the sort/consolidate is mandatory, advance-by-lower is
  the only optional part.
- DESIGN.md S5: record the validation.

Docs + debug assert only; no behavior change. 14 tests green (asserts active).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…h, value_id)

Add `operators::int_proxy`: join and reduce operator tactics that run DD's
interesting-time and difference logic entirely in integer-proxy space, deferring
all value semantics to a backend. Only two integers cross the seam:

  * key_hash — a system-wide-stable content hash of the key; the unit of
    independence, by which operators partition, order, and merge (collisions are
    an accepted risk the per-hash logic tolerates).
  * value_id — an ephemeral, per-computation intra-hash value identifier.

The tactics elicit these ids from the backend, do the time and difference math to
stage integer corrections, and re-invoke the backend with the same ids to produce
output — the backend never sees a timestamp.

  * ProxyReduceTactic: interesting-time determination + per-moment application in
    cross-key-batched rounds; peak memory O(window presentation), not
    O(times x values); differencing lives inside the backend (non-Abelian).
    ProxyReduceBackend: seed_times / begin / next_window / reduce_corrections /
    emit / finish.
  * ProxyJoinTactic: equi-join by key_hash, value work by matched id lists, output
    chunked at key_hash boundaries with mid-wave flush for high-fanout keys.
    ProxyJoinBackend: present0 / present1 / cross.

Correctness: a worked in-memory VecChunk backend, a grid-oracle fuzz checking
every product-time point, and a doubly-nested product-timed SCC differential test
vs stock reduce. DESIGN.md carries the model, the seam contracts, and the roadmap.

Performance (F7): a hash-native storage path (pre-map keys to hashes, values as
ids) makes `present` an in-order scan; retire-wide tactic buffer reuse, in-place
KeyState reload, and slice-based reads bring reduce load within ~1.3x and join
within ~1.2x of the stock cursor operators (profiled, samply/pollard).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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