Integer-proxy chunks: backend-agnostic join and reduce over (key_hash, value_id)#781
Draft
frankmcsherry wants to merge 1 commit into
Draft
Integer-proxy chunks: backend-agnostic join and reduce over (key_hash, value_id)#781frankmcsherry wants to merge 1 commit into
frankmcsherry wants to merge 1 commit into
Conversation
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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_hashis 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_idis 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-lessChunkof proxy columns (slots into the Chunk navigation capability #778 split; batches, fueled merging, and grading come free, entirely in integer space), plusfrom_unsorted— integer sort+consolidate with representative provenance — as the helper a backend uses to build presentations.operators/int_proxy:ProxyJoinTactic/ProxyReduceTacticfor thejoin_with_tacticandreduce_with_tacticseams (madepubhere — 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-retirependingkeyed by the stablekey_hash, and restricts presentations to changed keys so incremental cost tracks the delta, not the accumulation. The module doc carries the design notes, including whyvalue_idis deliberately not order-preserving (min/max belong to the value callback).operators/int_proxy/reference: an in-memory backend overVecChunkarrangements with fnv content hashes, so the framework is exercised without any columnar engine.Product-time retire sequence exercising synthetic time corrections both within an interval and pended across retires.🤖 Generated with Claude Code