Split Builder along the seam its two users already follow - #846
Merged
Conversation
`Builder` had two consumers using disjoint halves of it. `MergeBatcher` called
only `seal`, and through it `with_capacity`, because it holds a whole chain and
can count keys, values, and updates before allocating. Reduce and upsert called
only `new`, `push`, and `done`, discovering their output key by key and never
sizing anything.
`seal` moves to a trait of its own:
pub trait Sealer<C> {
type Output;
fn seal(chain: &mut Vec<C>) -> Option<Self::Output>;
}
which is a `fn(&mut Vec<C>) -> Option<B>` in a form that can name `B`. It has no
receiver, so there is nowhere for an update to be retained between the chain
going in and the batch coming out.
`with_capacity` becomes inherent on the two `ord_neu` builders. It had no callers
outside `seal` and `new`, so it was never vocabulary; `new` now carries its own
one-line body instead of a default.
`MergeBatcher` bounds `Bu: Sealer<M::Chunk>` and stops naming `Builder`, which
also drops `Builder::Time` from its vocabulary — it never wanted it. The three
implementors implement both traits and share their machinery. No call site
changes: the aliases name the same types, and those types satisfy both bounds.
This splits one question into two. Whether reduce and upsert should take a
`Batcher` instead, and whether `Sealer` should be a function value rather than a
trait, can now be answered independently.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`new` was a second name for the empty builder, kept because the trait once offered `with_capacity` to prefer over it. It no longer does: sizing happens inside `Sealer::seal`, which owns its own allocation. So the preference the comment on `new` expressed is no longer expressible, or needed. `Default` says the same thing in the standard vocabulary. Reduce and upsert construct with `Bu::default()`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
frankmcsherry
force-pushed
the
builder-split
branch
from
August 26, 2026 20:34
e6d7b72 to
ebd8e49
Compare
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.
Builderhad two consumers using disjoint halves of it.MergeBatchercalled onlyseal, and through itwith_capacity, because it holds a whole chain and can count keys, values, and updates before allocating.Reduce and upsert called only
new,push, anddone, discovering their output key by key and never sizing anything.sealmoves to a trait of its own:which is a
fn(&mut Vec<C>) -> Option<B>in a form that can nameB.It has no receiver, so there is nowhere for an update to be retained between the chain going in and the batch coming out.
with_capacitybecomes inherent on the twoord_neubuilders.It had no callers outside
sealandnew, so it was never vocabulary;newnow carries its own one-line body instead of a default.MergeBatcherboundsBu: Sealer<M::Chunk>and stops namingBuilder, which also dropsBuilder::Timefrom its vocabulary — it never wanted it.The three implementors implement both traits and share their machinery.
No call site changes: the aliases name the same types, and those types satisfy both bounds.
The diff is four files, all under
trace/.This splits one question into two.
Whether reduce and upsert should take a
Batcherinstead, and whetherSealershould be a function value rather than a trait, can now be answered independently.Builds warning-free, workspace tests and doctests pass.