Skip to main content

dbsp/
trace.rs

1//! # Traces
2//!
3//! A "trace" describes how a collection of key-value pairs changes over time.
4//! A "batch" is a mostly immutable trace.  This module provides traits and
5//! structures for expressing traces in DBSP as collections of `(key, val, time,
6//! diff)` tuples.
7//!
8//! The base trait for a trace is [`BatchReader`], which allows a trace to be
9//! read in sorted order by key and value.  `BatchReader` provides [`Cursor`] to
10//! step through a batch's tuples without modifying them.
11//!
12//! The [`Batch`] trait extends [`BatchReader`] with types and methods for
13//! creating new traces from ordered tuples ([`Batch::Builder`]) or unordered
14//! tuples ([`Batch::Batcher`]), or by merging traces of like types.
15//!
16//! The [`Trace`] trait, which also extends [`BatchReader`], adds methods to
17//! append new batches.  New tuples must not have times earlier than any of the
18//! tuples already in the trace.
19//!
20//! # Time within traces
21//!
22//! See the [time](crate::time) module documentation for a description of
23//! logical times.
24//!
25//! Traces are sorted by key and value.  They are not sorted with respect to
26//! time: reading a trace might obtain out of order and duplicate times among
27//! the `(time, diff)` pairs associated with a key and value.
28
29use crate::circuit::metadata::OperatorMeta;
30use crate::dynamic::{ClonableTrait, DynDataTyped, DynUnit, Weight};
31use crate::storage::buffer_cache::CacheStats;
32use crate::storage::file::SerializerInner;
33use crate::storage::file::TouchedWindowCount;
34pub use crate::storage::file::{DbspSerializer, Deserializable, Deserializer, Rkyv};
35use crate::storage::file::{FilterKind, FilterStats};
36use crate::trace::cursor::{
37    DefaultPushCursor, FilteredMergeCursor, FilteredMergeCursorWithSnapshot, PushCursor,
38    UnfilteredMergeCursor,
39};
40use crate::utils::{IsNone, SupportsRoaring};
41use crate::{dynamic::ArchivedDBData, storage::buffer_cache::FBuf};
42use cursor::CursorFactory;
43use enum_map::Enum;
44use feldera_storage::fbuf::FBufSerializer;
45use feldera_storage::{FileCommitter, FileReader, StoragePath};
46use rand::{Rng, thread_rng};
47use rkyv::ser::Serializer as _;
48use size_of::SizeOf;
49use std::any::TypeId;
50use std::future::Future;
51use std::sync::Arc;
52use std::{fmt::Debug, hash::Hash};
53
54pub mod cursor;
55pub mod filter;
56pub mod layers;
57pub mod ord;
58mod sampling;
59pub mod spine_async;
60pub(crate) use sampling::sample_keys_from_batches;
61pub use spine_async::{BatchReaderWithSnapshot, ListMerger, Spine, SpineSnapshot, WithSnapshot};
62
63#[cfg(test)]
64pub mod test;
65
66pub use ord::{
67    FallbackIndexedWSet, FallbackIndexedWSetBuilder, FallbackIndexedWSetFactories,
68    FallbackKeyBatch, FallbackKeyBatchFactories, FallbackValBatch, FallbackValBatchFactories,
69    FallbackWSet, FallbackWSetBuilder, FallbackWSetFactories, FileIndexedWSet,
70    FileIndexedWSetFactories, FileKeyBatch, FileKeyBatchFactories, FileValBatch,
71    FileValBatchFactories, FileWSet, FileWSetFactories, OrdIndexedWSet, OrdIndexedWSetBuilder,
72    OrdIndexedWSetFactories, OrdKeyBatch, OrdKeyBatchFactories, OrdValBatch, OrdValBatchFactories,
73    OrdWSet, OrdWSetBuilder, OrdWSetFactories, VecIndexedWSet, VecIndexedWSetFactories,
74    VecKeyBatch, VecKeyBatchFactories, VecValBatch, VecValBatchFactories, VecWSet,
75    VecWSetFactories,
76};
77
78use rkyv::bytecheck;
79use rkyv::{Deserialize, archived_root};
80
81use crate::{
82    Error, NumEntries, Timestamp,
83    algebra::MonoidValue,
84    dynamic::{DataTrait, DynPair, DynVec, DynWeightedPairs, Erase, Factory, WeightTrait},
85    storage::file::reader::Error as ReaderError,
86};
87pub use cursor::{Cursor, MergeCursor};
88pub use filter::{BatchFilterStats, BatchFilters, Filter, GroupFilter};
89pub use layers::Trie;
90
91/// Trait for data stored in batches.
92///
93/// This trait is used as a bound on `BatchReader::Key` and `BatchReader::Val`
94/// associated types (see [`trait BatchReader`]).  Hence when writing code that
95/// must be generic over any relational data, it is sufficient to impose
96/// `DBData` as a trait bound on types.  Conversely, a trait bound of the form
97/// `B: BatchReader` implies `B::Key: DBData` and `B::Val: DBData`.
98pub trait DBData:
99    Default
100    + Clone
101    + Eq
102    + Ord
103    + Hash
104    + SizeOf
105    + Send
106    + Sync
107    + Debug
108    + ArchivedDBData
109    + IsNone<Inner: ArchivedDBData>
110    + SupportsRoaring
111    + 'static
112{
113}
114
115/// Automatically implement DBData for everything that satisfied the bounds.
116impl<T> DBData for T where
117    T: Default
118        + Clone
119        + Eq
120        + Ord
121        + Hash
122        + SizeOf
123        + Send
124        + Sync
125        + Debug
126        + ArchivedDBData
127        + IsNone<Inner: ArchivedDBData>
128        + SupportsRoaring
129        + 'static
130{
131}
132
133/// A spine that is serialized to a file.
134#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
135#[archive_attr(derive(rkyv::CheckBytes))]
136pub(crate) struct CommittedSpine {
137    pub batches: Vec<String>,
138    pub merged: Vec<(String, String)>,
139    pub effort: u64,
140    pub dirty: bool,
141}
142
143/// Deserializes `bytes` as type `T` using `rkyv`, tolerating `bytes` being
144/// misaligned.
145pub fn unaligned_deserialize<T: Deserializable>(bytes: &[u8]) -> T {
146    let mut aligned_bytes = FBuf::new();
147    aligned_bytes.extend_from_slice(bytes);
148    aligned_deserialize(&aligned_bytes)
149}
150
151/// Deserializes `bytes` as type `T` using `rkyv`.  `bytes` must be properly
152/// aligned.
153pub fn aligned_deserialize<T: Deserializable>(bytes: &[u8]) -> T {
154    unsafe { archived_root::<T>(bytes) }
155        .deserialize(&mut Deserializer::default())
156        .unwrap()
157}
158
159/// Trait for data types used as weights.
160///
161/// A type used for weights in a batch (i.e., as `BatchReader::R`) must behave
162/// as a monoid, i.e., a set with an associative `+` operation and a neutral
163/// element (zero).
164///
165/// Some applications use a weight as a ring, that is, require it to support
166/// multiplication too.
167///
168/// Finally, some applications require it to have `<` and `>` operations, in
169/// particular to distinguish whether something is an insertion or deletion.
170///
171/// Signed integer types such as `i32` and `i64` are suitable as weights,
172/// although if there is overflow then the results will be wrong.
173///
174/// When writing code generic over any weight type, it is sufficient to impose
175/// `DBWeight` as a trait bound on types.  Conversely, a trait bound of the form
176/// `B: BatchReader` implies `B::R: DBWeight`.
177pub trait DBWeight: DBData + MonoidValue {}
178impl<T> DBWeight for T where T: DBData + MonoidValue {}
179
180pub trait BatchReaderFactories<
181    K: DataTrait + ?Sized,
182    V: DataTrait + ?Sized,
183    T,
184    R: WeightTrait + ?Sized,
185>: Clone + Send + Sync
186{
187    // type BatchItemVTable: BatchItemTypeDescr<Key = K, Val = V, Item = I, R = R>;
188    fn new<KType, VType, RType>() -> Self
189    where
190        KType: DBData + Erase<K>,
191        VType: DBData + Erase<V>,
192        RType: DBWeight + Erase<R>;
193
194    fn key_factory(&self) -> &'static dyn Factory<K>;
195    fn keys_factory(&self) -> &'static dyn Factory<DynVec<K>>;
196    fn val_factory(&self) -> &'static dyn Factory<V>;
197    fn weight_factory(&self) -> &'static dyn Factory<R>;
198}
199
200// TODO: use Tuple3 instead
201pub type WeightedItem<K, V, R> = DynPair<DynPair<K, V>, R>;
202
203pub trait BatchFactories<K: DataTrait + ?Sized, V: DataTrait + ?Sized, T, R: WeightTrait + ?Sized>:
204    BatchReaderFactories<K, V, T, R>
205{
206    fn item_factory(&self) -> &'static dyn Factory<DynPair<K, V>>;
207
208    fn weighted_items_factory(&self) -> &'static dyn Factory<DynWeightedPairs<DynPair<K, V>, R>>;
209    fn weighted_vals_factory(&self) -> &'static dyn Factory<DynWeightedPairs<V, R>>;
210    fn weighted_item_factory(&self) -> &'static dyn Factory<WeightedItem<K, V, R>>;
211
212    /// Factory for a vector of (T, R) or `None` if `T` is `()`.
213    fn time_diffs_factory(
214        &self,
215    ) -> Option<&'static dyn Factory<DynWeightedPairs<DynDataTyped<T>, R>>>;
216}
217
218/// A set of `(key, val, time, diff)` tuples that can be read and extended.
219///
220/// `Trace` extends [`BatchReader`], most notably with [`insert`][Self::insert]
221/// for adding new batches of tuples.
222///
223/// See [crate documentation](crate::trace) for more information on batches and
224/// traces.
225pub trait Trace: BatchReader {
226    /// The type of an immutable collection of updates.
227    type Batch: Batch<
228            Key = Self::Key,
229            Val = Self::Val,
230            Time = Self::Time,
231            R = Self::R,
232            Factories = Self::Factories,
233        >;
234
235    /// Allocates a new empty trace associated with `name`, which should
236    /// identify the operator or other origin of the trace.
237    fn new(factories: &Self::Factories, name: Arc<String>) -> Self;
238
239    /// Updates the name of the trace to `name`.
240    fn set_name(&mut self, name: Arc<String>);
241
242    /// Sets a compaction frontier, i.e., a timestamp such that timestamps
243    /// below the frontier are indistinguishable to DBSP, therefore any `ts`
244    /// in the trace can be safely replaced with `ts.join(frontier)` without
245    /// affecting the output of the circuit.  By applying this replacement,
246    /// updates to the same (key, value) pairs applied during different steps
247    /// can be merged or discarded.
248    ///
249    /// The compaction is performed lazily at merge time.
250    fn set_frontier(&mut self, frontier: &Self::Time);
251
252    /// Exert merge effort, even without updates.
253    fn exert(&mut self, effort: &mut isize);
254
255    /// Merge all updates in a trace into a single batch.
256    fn consolidate(self) -> Option<Self::Batch>;
257
258    /// Introduces a batch of updates to the trace.
259    ///
260    /// If the trace has too many unmerged batches, this method will block
261    /// (asynchronously) until some of them have been merged.
262    fn insert(&mut self, batch: impl Into<Arc<Self::Batch>>) -> impl Future<Output = ()>;
263
264    /// Inserts a batch into the spine without blocking.  Thus, this omits:
265    ///
266    /// - Spilling the batch to storage when that is a good idea.
267    ///
268    /// - Waiting until the number of batches in the spine falls below the level
269    ///   at which we impose backpressure.  The function returns true if
270    ///   backpressure is warranted.  The caller may do so afterward by calling
271    ///   [Self::backpressure_wait].
272    fn insert_without_blocking(&mut self, batch: impl Into<Arc<Self::Batch>>) -> bool;
273
274    /// Waits for the number of batches in the spine to fall below the level at
275    /// which we impose backpressure.
276    fn backpressure_wait(&self) -> impl Future<Output = ()>;
277
278    /// Clears the value of the "dirty" flag to `false`.
279    ///
280    /// The "dirty" flag is used to efficiently track changes to the trace,
281    /// e.g., as part of checking whether a circuit has reached a fixed point.
282    /// Pushing a non-empty batch to the trace sets the flag to `true`. The
283    /// [`Self::dirty`] method returns true iff the trace has changed since the
284    /// last call to `clear_dirty_flag`.
285    fn clear_dirty_flag(&mut self);
286
287    /// Returns the value of the dirty flag.
288    fn dirty(&self) -> bool;
289
290    /// Informs the trace that keys that don't pass the filter are no longer
291    /// used and can be removed from the trace.
292    ///
293    /// The implementation is not required to remove truncated keys instantly
294    /// or at all.  This method is just a hint that keys that don't pass the
295    /// filter are no longer of interest to the consumer of the trace and
296    /// can be garbage collected.
297    ///
298    /// # Rationale
299    ///
300    /// This API is similar to the old API `BatchReader::truncate_keys_below`,
301    /// but in [Trace] instead of [BatchReader].  The difference is that a batch
302    /// can truncate its keys instanly by simply moving an internal pointer to
303    /// the first remaining key.  However, there is no similar way to retain
304    /// keys based on arbitrary predicates, this can only be done efficiently as
305    /// part of trace maintenance when either merging or compacting batches.
306    fn retain_keys(&mut self, filter: Filter<Self::Key>);
307
308    /// Informs the trace that values that don't pass the filter are no longer
309    /// used and can be removed from the trace.
310    ///
311    /// The implementation is not required to remove truncated values instantly
312    /// or at all.  This method is just a hint that values that don't pass the
313    /// filter are no longer of interest to the consumer of the trace and
314    /// can be garbage collected.
315    fn retain_values(&mut self, filter: GroupFilter<Self::Val>);
316
317    fn key_filter(&self) -> &Option<Filter<Self::Key>>;
318    fn value_filter(&self) -> &Option<GroupFilter<Self::Val>>;
319
320    /// Writes this trace to storage beneath `base`, using `pid` as a file name
321    /// prefix.  Adds the files that were written to `files` so that they can be
322    /// committed later.
323    fn save(
324        &mut self,
325        base: &StoragePath,
326        pid: &str,
327        files: &mut Vec<Arc<dyn FileCommitter>>,
328    ) -> Result<(), Error>;
329
330    /// Reads this trace back from storage under `base` with `pid` as the
331    /// prefix.
332    fn restore(&mut self, base: &StoragePath, pid: &str) -> Result<(), Error>;
333
334    /// Allows the trace to report additional metadata.
335    fn metadata(&self, _meta: &mut OperatorMeta) {}
336
337    fn initiate_compaction(&self);
338
339    /// Returns `true` when compaction has fully converged: all compaction
340    /// requests have been processed, no background merge is in progress, and
341    /// the trace has been reduced to at most one batch.
342    fn is_compaction_complete(&self) -> bool;
343}
344
345/// Where a batch is stored.
346#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
347pub enum BatchLocation {
348    /// In RAM.
349    Memory,
350
351    /// On disk.
352    Storage,
353}
354
355// impl BatchLocation {
356//     fn as_str(&self) -> &'static str {
357//         match self {
358//             Self::Memory => "memory",
359//             Self::Storage => "storage",
360//         }
361//     }
362// }
363
364/// A set of `(key, value, time, diff)` tuples whose contents may be read in
365/// order by key and value.
366///
367/// A `BatchReader` is a mostly read-only interface.  This is especially useful
368/// for views derived from other sources in ways that prevent the construction
369/// of batches from the type of data in the view (for example, filtered views,
370/// or views with extended time coordinates).
371///
372/// See [crate documentation](crate::trace) for more information on batches and
373/// traces.
374///
375/// # Object safety
376///
377/// `BatchReader` is not object safe (it cannot be used as `dyn BatchReader`),
378/// but [Cursor] is, which can often be a useful substitute.
379pub trait BatchReader: Debug + NumEntries + Rkyv + SizeOf + 'static
380where
381    Self: Sized,
382{
383    type Factories: BatchFactories<Self::Key, Self::Val, Self::Time, Self::R>;
384
385    /// Key by which updates are indexed.
386    type Key: DataTrait + ?Sized;
387
388    /// Values associated with keys.
389    type Val: DataTrait + ?Sized;
390
391    /// Timestamps associated with updates
392    type Time: Timestamp;
393
394    /// Associated update.
395    type R: WeightTrait + ?Sized;
396
397    /// The type used to enumerate the batch's contents.
398    type Cursor<'s>: Cursor<Self::Key, Self::Val, Self::Time, Self::R> + Clone + Send
399    where
400        Self: 's;
401
402    // type Consumer: Consumer<Self::Key, Self::Val, Self::R, Self::Time>;
403
404    fn factories(&self) -> Self::Factories;
405
406    /// Acquires a cursor to the batch's contents.
407    fn cursor(&self) -> Self::Cursor<'_>;
408
409    /// Acquires a [PushCursor] for the batch's contents.
410    fn push_cursor(
411        &self,
412    ) -> Box<dyn PushCursor<Self::Key, Self::Val, Self::Time, Self::R> + Send + '_> {
413        Box::new(DefaultPushCursor::new(self.cursor()))
414    }
415
416    /// Acquires a [MergeCursor] for the batch's contents.
417    fn merge_cursor(
418        &self,
419        key_filter: Option<Filter<Self::Key>>,
420        value_filter: Option<GroupFilter<Self::Val>>,
421    ) -> Box<dyn MergeCursor<Self::Key, Self::Val, Self::Time, Self::R> + Send + '_> {
422        if key_filter.is_none() && value_filter.is_none() {
423            Box::new(UnfilteredMergeCursor::new(self.cursor()))
424        } else if let Some(GroupFilter::Simple(filter)) = value_filter {
425            Box::new(FilteredMergeCursor::new(
426                self.cursor(),
427                key_filter,
428                Some(filter),
429            ))
430        } else {
431            // Other forms of GroupFilters cannot be evaluated without a trace snapshot -- don't filter values
432            // in such cursors.
433            Box::new(FilteredMergeCursor::new(self.cursor(), key_filter, None))
434        }
435    }
436
437    /// Similar to `merge_cursor`, but invoked in the context of a spine merger.
438    /// Takes the current spine snapshot as an extra argument and uses it to evaluate `value_filter` precisely.
439    fn merge_cursor_with_snapshot<'a, S>(
440        &'a self,
441        key_filter: Option<Filter<Self::Key>>,
442        value_filter: Option<GroupFilter<Self::Val>>,
443        snapshot: &'a Option<Arc<S>>,
444    ) -> Box<dyn MergeCursor<Self::Key, Self::Val, Self::Time, Self::R> + Send + 'a>
445    where
446        S: BatchReader<Key = Self::Key, Val = Self::Val, Time = Self::Time, R = Self::R>,
447    {
448        let Some(snapshot) = snapshot else {
449            return self.merge_cursor(key_filter, value_filter);
450        };
451        if key_filter.is_none() && value_filter.is_none() {
452            Box::new(UnfilteredMergeCursor::new(self.cursor()))
453        } else if value_filter.is_none() {
454            Box::new(FilteredMergeCursor::new(self.cursor(), key_filter, None))
455        } else if let Some(GroupFilter::Simple(filter)) = value_filter {
456            Box::new(FilteredMergeCursor::new(
457                self.cursor(),
458                key_filter,
459                Some(filter),
460            ))
461        } else {
462            Box::new(FilteredMergeCursorWithSnapshot::new(
463                self.cursor(),
464                key_filter,
465                value_filter.unwrap(),
466                snapshot,
467            ))
468        }
469    }
470
471    /// Acquires a merge cursor for the batch's contents.
472    fn consuming_cursor(
473        &mut self,
474        key_filter: Option<Filter<Self::Key>>,
475        value_filter: Option<GroupFilter<Self::Val>>,
476    ) -> Box<dyn MergeCursor<Self::Key, Self::Val, Self::Time, Self::R> + Send + '_> {
477        self.merge_cursor(key_filter, value_filter)
478    }
479    //fn consumer(self) -> Self::Consumer;
480
481    /// The number of keys in the batch.
482    // TODO: return `(usize, Option<usize>)`, similar to
483    // `Iterator::size_hint`, since not all implementations
484    // can compute the number of keys precisely.  Same for
485    // `len()`.
486    fn key_count(&self) -> usize;
487
488    /// The number of updates in the batch.
489    fn len(&self) -> usize;
490
491    /// The memory or storage size of the batch in bytes.
492    ///
493    /// This can be an approximation, such as the size of an on-disk file for a
494    /// stored batch.
495    ///
496    /// Implementations of this function can be expensive because they might
497    /// require iterating through all the data in a batch.  Currently this is
498    /// only used to decide whether to keep the result of a merge in memory or
499    /// on storage.  For this case, the merge will visit and copy all the data
500    /// in the batch. The batch will be discarded afterward, which means that
501    /// the implementation need not attempt to cache the return value.
502    fn approximate_byte_size(&self) -> usize;
503
504    /// Statistics of the secondary membership filter used by
505    /// [Cursor::seek_key_exact] after the range filter.
506    ///
507    /// Today this is usually a Bloom filter. Batches without such a filter
508    /// should return zero/default stats.
509    fn membership_filter_stats(&self) -> FilterStats {
510        FilterStats::default()
511    }
512
513    /// Filter kind for the secondary membership filter used by
514    /// [Cursor::seek_key_exact].
515    fn membership_filter_kind(&self) -> FilterKind {
516        FilterKind::None
517    }
518
519    /// Statistics of the in-memory range filter used by
520    /// [Cursor::seek_key_exact].
521    ///
522    /// Returns range-filter stats. Batches without a range filter should
523    /// return zeroed range stats.
524    fn range_filter_stats(&self) -> FilterStats {
525        FilterStats::default()
526    }
527
528    /// Where the batch's data is stored.
529    fn location(&self) -> BatchLocation {
530        BatchLocation::Memory
531    }
532
533    /// Storage cache access statistics for this batch only.
534    ///
535    /// Most batches are in-memory, so they don't have any statistics.
536    fn cache_stats(&self) -> CacheStats {
537        CacheStats::default()
538    }
539
540    /// True if the batch is empty.
541    fn is_empty(&self) -> bool {
542        self.len() == 0
543    }
544
545    /// Returns a uniform random sample of distincts keys from the batch.
546    ///
547    /// Does not take into account the number values associated with each
548    /// key and their weights, i.e., a key that has few values is as likely
549    /// to appear in the output sample as a key with many values.
550    ///
551    /// # Arguments
552    ///
553    /// * `rng` - random number generator used to generate the sample.
554    ///
555    /// * `sample_size` - requested sample size.
556    ///
557    /// * `sample` - output
558    ///
559    /// # Invariants
560    ///
561    /// The actual sample computed by the method can be smaller than
562    /// `sample_size` even if `self` contains `>sample_size` keys.
563    ///
564    /// A correct implementation must enforce the following invariants:
565    ///
566    /// * The output sample size cannot exceed `sample_size`.
567    ///
568    /// * The output sample can only contain keys present in `self` (with
569    ///   non-zero weights).
570    ///
571    /// * If `sample_size` is greater than or equal to the number of keys
572    ///   present in `self` (with non-zero weights), the resulting sample must
573    ///   contain all such keys.
574    ///
575    /// * The output sample contains keys sorted in ascending order.
576    fn sample_keys<RG>(&self, rng: &mut RG, sample_size: usize, sample: &mut DynVec<Self::Key>)
577    where
578        RG: Rng;
579
580    /// Returns num_partitions-1 keys from the batch that partition the batch into num_partitions
581    /// approximately equal size ranges 0..key1, key1..key2, ... , key_num_partitions-1..last_key_in_the_batch.
582    ///
583    /// The default implementation uses the sample_keys method to sample num_partitions^2 keys and
584    /// picks keys num_partitions, 2*num_partitions, ..,num_partitions-1*num_partitions as boundaries.
585    ///
586    /// # Arguments
587    ///
588    /// * `num_partitions` - number of partitions to create.
589    /// * `bounds` - output vector to store the partition boundaries.
590    fn partition_keys(&self, num_partitions: usize, bounds: &mut DynVec<Self::Key>)
591    where
592        Self::Time: PartialEq<()>,
593    {
594        bounds.clear();
595        if num_partitions <= 1 {
596            return;
597        }
598
599        let sample_size = num_partitions * num_partitions;
600
601        let mut sample = self.factories().keys_factory().default_box();
602        self.sample_keys(&mut thread_rng(), sample_size, sample.as_mut());
603
604        // Pick evenly distributed keys as boundaries
605        let sample_len = sample.len();
606        if sample_len == 0 {
607            return;
608        }
609
610        if sample_len >= num_partitions {
611            // Pick num_bounds evenly distributed indices from the sample
612            // These divide the sample into num_bounds + 1 roughly equal parts
613            for i in 0..num_partitions - 1 {
614                let idx = ((i + 1) * sample_len) / num_partitions;
615                let idx = idx.min(sample_len - 1);
616                bounds.push_ref(sample.index(idx));
617            }
618        } else {
619            // If we have fewer samples than needed, use what we have
620            for i in 0..sample_len {
621                bounds.push_ref(sample.index(i));
622            }
623        }
624    }
625
626    /// Creates and returns a new batch that is a subset of this one, containing
627    /// only the key-value pairs whose keys are in `keys`. May also return
628    /// `None`, the default implementation, if the batch doesn't want to
629    /// implement this method.  In particular, a batch for which access through
630    /// a cursor is fast should return `None` to avoid the expense of copying
631    /// data.
632    ///
633    /// # Rationale
634    ///
635    /// This method enables performance optimizations for the case where these
636    /// assumptions hold:
637    ///
638    /// 1. Individual [Batch]es flowing through a circuit are small enough to
639    ///    fit comfortably in memory.
640    ///
641    /// 2. [Trace]s accumulated over time as a circuit executes may become large
642    ///    enough that they must be maintained in external storage.
643    ///
644    /// If an operator needs to fetch all of the data from a `trace` that
645    /// corresponds to some set of `keys`, then, given these assumptions, doing
646    /// so one key at a time with a cursor will be slow because every key fetch
647    /// potentially incurs a round trip to the storage, with total latency O(n)
648    /// in the number of keys. This method gives the batch implementation the
649    /// opportunity to implement parallel fetch for `trace.fetch(key)`, with
650    /// total latency O(1) in the number of keys.
651    #[allow(async_fn_in_trait)]
652    async fn fetch<B>(
653        &self,
654        keys: &B,
655    ) -> Option<Box<dyn CursorFactory<Self::Key, Self::Val, Self::Time, Self::R>>>
656    where
657        B: BatchReader<Key = Self::Key, Time = ()>,
658    {
659        let _ = keys;
660        None
661    }
662
663    fn keys(&self) -> Option<&DynVec<Self::Key>> {
664        None
665    }
666}
667
668impl<B> BatchReader for Arc<B>
669where
670    B: BatchReader,
671{
672    type Factories = B::Factories;
673    type Key = B::Key;
674    type Val = B::Val;
675    type Time = B::Time;
676    type R = B::R;
677    type Cursor<'s> = B::Cursor<'s>;
678    fn factories(&self) -> Self::Factories {
679        (**self).factories()
680    }
681    fn cursor(&self) -> Self::Cursor<'_> {
682        (**self).cursor()
683    }
684    fn merge_cursor(
685        &self,
686        key_filter: Option<Filter<Self::Key>>,
687        value_filter: Option<GroupFilter<Self::Val>>,
688    ) -> Box<dyn MergeCursor<Self::Key, Self::Val, Self::Time, Self::R> + Send + '_> {
689        (**self).merge_cursor(key_filter, value_filter)
690    }
691    fn key_count(&self) -> usize {
692        (**self).key_count()
693    }
694    fn len(&self) -> usize {
695        (**self).len()
696    }
697    fn approximate_byte_size(&self) -> usize {
698        (**self).approximate_byte_size()
699    }
700    fn membership_filter_stats(&self) -> FilterStats {
701        (**self).membership_filter_stats()
702    }
703    fn membership_filter_kind(&self) -> FilterKind {
704        (**self).membership_filter_kind()
705    }
706    fn range_filter_stats(&self) -> FilterStats {
707        (**self).range_filter_stats()
708    }
709    fn location(&self) -> BatchLocation {
710        (**self).location()
711    }
712    fn cache_stats(&self) -> CacheStats {
713        (**self).cache_stats()
714    }
715    fn is_empty(&self) -> bool {
716        (**self).is_empty()
717    }
718    fn sample_keys<RG>(&self, rng: &mut RG, sample_size: usize, sample: &mut DynVec<Self::Key>)
719    where
720        RG: Rng,
721    {
722        (**self).sample_keys(rng, sample_size, sample)
723    }
724    fn consuming_cursor(
725        &mut self,
726        key_filter: Option<Filter<Self::Key>>,
727        value_filter: Option<GroupFilter<Self::Val>>,
728    ) -> Box<dyn MergeCursor<Self::Key, Self::Val, Self::Time, Self::R> + Send + '_> {
729        (**self).merge_cursor(key_filter, value_filter)
730    }
731    async fn fetch<KB>(
732        &self,
733        keys: &KB,
734    ) -> Option<Box<dyn CursorFactory<Self::Key, Self::Val, Self::Time, Self::R>>>
735    where
736        KB: BatchReader<Key = Self::Key, Time = ()>,
737    {
738        (**self).fetch(keys).await
739    }
740    fn keys(&self) -> Option<&DynVec<Self::Key>> {
741        (**self).keys()
742    }
743}
744
745/// A [`BatchReader`] plus features for constructing new batches.
746///
747/// [`Batch`] extends [`BatchReader`] with types for constructing new batches
748/// from ordered tuples ([`Self::Builder`]) or unordered tuples
749/// ([`Self::Batcher`]), or by merging traces of like types, plus some
750/// convenient methods for using those types.
751///
752/// See [crate documentation](crate::trace) for more information on batches and
753/// traces.
754pub trait Batch: BatchReader + Clone + Send + Sync
755where
756    Self: Sized,
757{
758    /// A batch type equivalent to `Self`, but with timestamp type `T` instead of `Self::Time`.
759    type Timed<T: Timestamp>: Batch<
760            Key = <Self as BatchReader>::Key,
761            Val = <Self as BatchReader>::Val,
762            Time = T,
763            R = <Self as BatchReader>::R,
764        >;
765
766    /// A type used to assemble batches from disordered updates.
767    type Batcher: Batcher<Self>;
768
769    /// A type used to assemble batches from ordered update sequences.
770    type Builder: Builder<Self>;
771
772    /// Assemble an unordered vector of weighted items into a batch.
773    #[allow(clippy::type_complexity)]
774    fn dyn_from_tuples(
775        factories: &Self::Factories,
776        time: Self::Time,
777        tuples: &mut Box<DynWeightedPairs<DynPair<Self::Key, Self::Val>, Self::R>>,
778    ) -> Self {
779        let mut batcher = Self::Batcher::new_batcher(factories, time);
780        batcher.push_batch(tuples);
781        batcher.seal()
782    }
783
784    /// Creates a new batch as a copy of `batch`, using `timestamp` for all of
785    /// the new batch's timestamps This is useful for adding a timestamp to a
786    /// batch, or for converting between different batch implementations
787    /// (e.g. writing an in-memory batch to disk).
788    ///
789    /// TODO: for adding a timestamp to a batch, this could be implemented more
790    /// efficiently by having a special batch type where all updates have the same
791    /// timestamp, as this is the only kind of batch that we ever create directly in
792    /// DBSP; batches with multiple timestamps are only created as a result of
793    /// merging.  The main complication is that we will need to extend the trace
794    /// implementation to work with batches of multiple types.  This shouldn't be
795    /// too hard and is on the todo list.
796    fn from_batch<BI>(batch: &BI, timestamp: &Self::Time, factories: &Self::Factories) -> Self
797    where
798        BI: BatchReader<Key = Self::Key, Val = Self::Val, Time = (), R = Self::R>,
799    {
800        // Source and destination types are usually the same in the top-level scope.
801        // Optimize for this case by simply cloning the source batch. If the batch is
802        // implemented as `Arc` internally, this is essentially zero cost.
803        if TypeId::of::<BI>() == TypeId::of::<Self>() {
804            unsafe { std::mem::transmute::<&BI, &Self>(batch).clone() }
805        } else {
806            Self::from_cursor(
807                batch.cursor(),
808                timestamp,
809                factories,
810                batch.key_count(),
811                batch.len(),
812            )
813        }
814    }
815
816    /// Like `from_batch`, but avoids cloning the batch if the output type is identical to the input type.
817    fn from_arc_batch<BI>(
818        batch: &Arc<BI>,
819        timestamp: &Self::Time,
820        factories: &Self::Factories,
821    ) -> Arc<Self>
822    where
823        BI: BatchReader<Key = Self::Key, Val = Self::Val, Time = (), R = Self::R>,
824    {
825        // Source and destination types are usually the same in the top-level scope.
826        // Optimize for this case by simply cloning the source batch. If the batch is
827        // implemented as `Arc` internally, this is essentially zero cost.
828        if TypeId::of::<BI>() == TypeId::of::<Self>() {
829            unsafe { std::mem::transmute::<&Arc<BI>, &Arc<Self>>(batch).clone() }
830        } else {
831            Arc::new(Self::from_cursor(
832                batch.cursor(),
833                timestamp,
834                factories,
835                batch.key_count(),
836                batch.len(),
837            ))
838        }
839    }
840
841    /// Creates a new batch as a copy of the tuples accessible via `cursor``,
842    /// using `timestamp` for all of the new batch's timestamps.
843    fn from_cursor<C>(
844        mut cursor: C,
845        timestamp: &Self::Time,
846        factories: &Self::Factories,
847        key_capacity: usize,
848        value_capacity: usize,
849    ) -> Self
850    where
851        C: Cursor<Self::Key, Self::Val, (), Self::R>,
852    {
853        let mut builder = Self::Builder::with_capacity(factories, key_capacity, value_capacity);
854        while cursor.key_valid() {
855            let mut any_values = false;
856            while cursor.val_valid() {
857                let weight = cursor.weight();
858                debug_assert!(!weight.is_zero());
859                builder.push_time_diff(timestamp, weight);
860                builder.push_val(cursor.val());
861                any_values = true;
862                cursor.step_val();
863            }
864            if any_values {
865                builder.push_key(cursor.key());
866            }
867            cursor.step_key();
868        }
869        builder.done()
870    }
871
872    /// Creates an empty batch.
873    fn dyn_empty(factories: &Self::Factories) -> Self {
874        Self::Builder::new_builder(factories).done()
875    }
876
877    /// Returns elements from `self` that satisfy a predicate.
878    fn filter(&self, predicate: &dyn Fn(&Self::Key, &Self::Val) -> bool) -> Self
879    where
880        Self::Time: PartialEq<()> + From<()>,
881    {
882        let factories = self.factories();
883        let mut builder = Self::Builder::new_builder(&factories);
884        let mut cursor = self.cursor();
885
886        while cursor.key_valid() {
887            let mut any_values = false;
888            while cursor.val_valid() {
889                if predicate(cursor.key(), cursor.val()) {
890                    builder.push_diff(cursor.weight());
891                    builder.push_val(cursor.val());
892                    any_values = true;
893                }
894                cursor.step_val();
895            }
896            if any_values {
897                builder.push_key(cursor.key());
898            }
899            cursor.step_key();
900        }
901
902        builder.done()
903    }
904
905    /// If this batch is not on storage, but supports writing itself to storage,
906    /// this method writes it to storage and returns the stored version.
907    fn persisted(&self) -> Option<Self> {
908        None
909    }
910
911    /// This functions returns the file that can be used to restore the batch's
912    /// contents.
913    ///
914    /// If the batch can not be persisted, this function returns None.
915    fn file_reader(&self) -> Option<Arc<dyn FileReader>> {
916        None
917    }
918
919    fn from_path(_factories: &Self::Factories, _path: &StoragePath) -> Result<Self, ReaderError> {
920        Err(ReaderError::Unsupported)
921    }
922
923    /// Minimum and maximum keys in this batch.
924    ///
925    /// File-backed batches materialize these bounds at write time. In-memory
926    /// batches compute them from their ordered key storage. Merge builders
927    /// use these bounds to decide whether a batch span can be encoded into a
928    /// roaring bitmap.
929    ///
930    /// Returns `None` for empty batches.
931    fn key_bounds(&self) -> Option<(&Self::Key, &Self::Key)>;
932
933    /// Exact number of 16-bit roaring windows touched by the batch, relative
934    /// to the batch minimum.
935    ///
936    /// File-backed batches materialize this count at write time. In-memory
937    /// batches track it while they are built. The merge-time filter predictor
938    /// uses it together with min/max span overlap to estimate how many roaring
939    /// containers the merged batch will likely touch.
940    ///
941    /// A value of `0` means the batch cannot provide an exact count, e.g. the
942    /// key type is not roaring-compatible, the batch span does not fit in
943    /// `u32`, or the batch is empty.
944    fn touched_window_count(&self) -> TouchedWindowCount;
945
946    /// The number of tuples with negative weights in the batch.
947    ///
948    /// This metric is used in merger heuristics. Negative weights are likely to cancel out
949    /// with positive weights when merging the batch with other batches in the spine; therefore the
950    /// merger will merge such batches more aggressively than it otherwise would based on the batch
951    /// size only.
952    ///
953    /// This heuristic is not useful for all batch types, in particular negative weights in batches
954    /// produced by recursive queries do not generally cancel out. For such batches we don't track
955    /// negative weights, and this method returns `None`.
956    fn negative_weight_count(&self) -> Option<u64>;
957}
958
959/// Functionality for collecting and batching updates.
960pub trait Batcher<Output>: SizeOf
961where
962    Output: Batch,
963{
964    /// Allocates a new empty batcher.  All tuples in the batcher (and its
965    /// output batch) will have timestamp `time`.
966    fn new_batcher(vtables: &Output::Factories, time: Output::Time) -> Self;
967
968    /// Adds an unordered batch of elements to the batcher.
969    fn push_batch(
970        &mut self,
971        batch: &mut Box<DynWeightedPairs<DynPair<Output::Key, Output::Val>, Output::R>>,
972    );
973
974    /// Adds a consolidated batch of elements to the batcher.
975    ///
976    /// A consolidated batch is sorted and contains no duplicates or zero
977    /// weights.
978    fn push_consolidated_batch(
979        &mut self,
980        batch: &mut Box<DynWeightedPairs<DynPair<Output::Key, Output::Val>, Output::R>>,
981    );
982
983    /// Returns the number of tuples in the batcher.
984    fn tuples(&self) -> usize;
985
986    /// Returns all updates not greater or equal to an element of `upper`.
987    fn seal(self) -> Output;
988}
989
990/// Functionality for building batches from ordered update sequences.
991///
992/// This interface requires the client to push all of the time-diff pairs
993/// associated with a value, then the value, then all the time-diff pairs
994/// associated with the next value, then that value, and so on. Once all of the
995/// values associated with the current key have been pushed, the client pushes
996/// the key.
997///
998/// If this interface is too low-level for the client, consider wrapping it in a
999/// [TupleBuilder].
1000///
1001/// # Example
1002///
1003/// To push the following tuples:
1004///
1005/// ```text
1006/// (k1, v1, t1, r1)
1007/// (k1, v1, t2, r2)
1008/// (k1, v2, t1, r1)
1009/// (k1, v3, t2, r2)
1010/// (k2, v1, t1, r1)
1011/// (k2, v1, t2, r2)
1012/// (k3, v1, t1, r1)
1013/// (k4, v2, t2, r2)
1014/// ```
1015///
1016/// the client would use:
1017///
1018/// ```ignore
1019/// builder.push_time_diff(t1, r1);
1020/// builder.push_time_diff(t2, r2);
1021/// builder.push_val(v1);
1022/// builder.push_time_diff(t1, r1);
1023/// builder.push_val(v2);
1024/// builder.push_time_diff(t2, r2);
1025/// builder.push_val(v3);
1026/// builder.push_key(k1);
1027/// builder.push_time_diff(t1, r1);
1028/// builder.push_time_diff(t2, r2);
1029/// builder.push_val(v1);
1030/// builder.push_key(k2);
1031/// builder.push_time_diff(t1, r1);
1032/// builder.push_val(v1);
1033/// builder.push_key(k3);
1034/// builder.push_time_diff(t2, r2);
1035/// builder.push_val(v2);
1036/// builder.push_key(k4);
1037/// ```
1038pub trait Builder<Output>: Send + SizeOf
1039where
1040    Self: Sized,
1041    Output: Batch,
1042{
1043    /// Creates a new builder with an initial capacity of 0.
1044    fn new_builder(factories: &Output::Factories) -> Self {
1045        Self::with_capacity(factories, 0, 0)
1046    }
1047
1048    /// Creates an empty builder with estimated capacities for keys and
1049    /// key-value pairs.  Only `tuple_capacity >= key_capacity` makes sense but
1050    /// implementations must tolerate contradictory capacity requests.
1051    ///
1052    /// The caller may optionally specify a preferred `location`.  The builder
1053    /// should honor it if it can, but some builders only build in one specific
1054    /// location.
1055    fn with_capacity_in_location(
1056        factories: &Output::Factories,
1057        key_capacity: usize,
1058        value_capacity: usize,
1059        location: Option<BatchLocation>,
1060    ) -> Self;
1061
1062    /// Creates an empty builder with estimated capacities for keys and
1063    /// key-value pairs.  Only `tuple_capacity >= key_capacity` makes sense but
1064    /// implementations must tolerate contradictory capacity requests.
1065    fn with_capacity(
1066        factories: &Output::Factories,
1067        key_capacity: usize,
1068        value_capacity: usize,
1069    ) -> Self {
1070        Self::with_capacity_in_location(factories, key_capacity, value_capacity, None)
1071    }
1072
1073    /// Creates an empty builder to hold the result of merging
1074    /// `batches`. Optionally, `location` can specify the preferred location for
1075    /// the result of the merge.
1076    fn for_merge<'a, B, I>(
1077        factories: &Output::Factories,
1078        batches: I,
1079        location: Option<BatchLocation>,
1080    ) -> Self
1081    where
1082        B: Batch<Key = Output::Key, Val = Output::Val, Time = Output::Time, R = Output::R>,
1083        I: IntoIterator<Item = &'a B> + Clone,
1084    {
1085        let key_capacity = batches.clone().into_iter().map(|b| b.key_count()).sum();
1086        let value_capacity = batches.into_iter().map(|b| b.len()).sum();
1087        Self::with_capacity_in_location(factories, key_capacity, value_capacity, location)
1088    }
1089
1090    /// Adds time-diff pair `(time, weight)`.
1091    fn push_time_diff(&mut self, time: &Output::Time, weight: &Output::R);
1092
1093    /// Adds time-diff pair `(time, weight)`.
1094    fn push_time_diff_mut(&mut self, time: &mut Output::Time, weight: &mut Output::R) {
1095        self.push_time_diff(time, weight);
1096    }
1097
1098    /// Adds value `val`.
1099    fn push_val(&mut self, val: &Output::Val);
1100
1101    /// Adds value `val`.
1102    fn push_val_mut(&mut self, val: &mut Output::Val) {
1103        self.push_val(val);
1104    }
1105
1106    /// Adds key `key`.
1107    fn push_key(&mut self, key: &Output::Key);
1108
1109    /// Adds key `key`.
1110    fn push_key_mut(&mut self, key: &mut Output::Key) {
1111        self.push_key(key);
1112    }
1113
1114    /// Adds time-diff pair `(), weight`.
1115    fn push_diff(&mut self, weight: &Output::R)
1116    where
1117        Output::Time: PartialEq<()>,
1118    {
1119        self.push_time_diff(&Output::Time::default(), weight);
1120    }
1121
1122    /// Adds time-diff pair `(), weight`.
1123    fn push_diff_mut(&mut self, weight: &mut Output::R)
1124    where
1125        Output::Time: PartialEq<()>,
1126    {
1127        self.push_diff(weight);
1128    }
1129
1130    /// Adds time-diff pair `(), weight` and value `val`.
1131    fn push_val_diff(&mut self, val: &Output::Val, weight: &Output::R)
1132    where
1133        Output::Time: PartialEq<()>,
1134    {
1135        self.push_time_diff(&Output::Time::default(), weight);
1136        self.push_val(val);
1137    }
1138
1139    /// Adds time-diff pair `(), weight` and value `val`.
1140    fn push_val_diff_mut(&mut self, val: &mut Output::Val, weight: &mut Output::R)
1141    where
1142        Output::Time: PartialEq<()>,
1143    {
1144        self.push_val_diff(val, weight);
1145    }
1146
1147    /// Allocates room for `additional` keys.
1148    fn reserve(&mut self, additional: usize) {
1149        let _ = additional;
1150    }
1151
1152    fn num_keys(&self) -> usize;
1153    fn num_tuples(&self) -> usize;
1154
1155    /// Completes building and returns the batch.
1156    fn done(self) -> Output;
1157}
1158
1159/// Batch builder that accepts a full tuple at a time.
1160///
1161/// This wrapper for [Builder] allows a full tuple to be added at a time.
1162pub struct TupleBuilder<B, Output>
1163where
1164    B: Builder<Output>,
1165    Output: Batch,
1166{
1167    builder: B,
1168    kv: Box<DynPair<Output::Key, Output::Val>>,
1169    has_kv: bool,
1170}
1171
1172impl<B, Output> TupleBuilder<B, Output>
1173where
1174    B: Builder<Output>,
1175    Output: Batch,
1176{
1177    pub fn new(factories: &Output::Factories, builder: B) -> Self {
1178        Self {
1179            builder,
1180            kv: factories.item_factory().default_box(),
1181            has_kv: false,
1182        }
1183    }
1184
1185    pub fn num_keys(&self) -> usize {
1186        self.builder.num_keys()
1187    }
1188
1189    pub fn num_tuples(&self) -> usize {
1190        self.builder.num_tuples()
1191    }
1192
1193    /// Adds `element` to the batch.
1194    pub fn push(&mut self, element: &mut DynPair<DynPair<Output::Key, Output::Val>, Output::R>)
1195    where
1196        Output::Time: PartialEq<()>,
1197    {
1198        let (kv, w) = element.split_mut();
1199        let (k, v) = kv.split_mut();
1200        self.push_vals(k, v, &mut Output::Time::default(), w);
1201    }
1202
1203    /// Adds tuple `(key, val, time, weight)` to the batch.
1204    pub fn push_refs(
1205        &mut self,
1206        key: &Output::Key,
1207        val: &Output::Val,
1208        time: &Output::Time,
1209        weight: &Output::R,
1210    ) {
1211        if self.has_kv {
1212            let (k, v) = self.kv.split_mut();
1213            if k != key {
1214                self.builder.push_val_mut(v);
1215                self.builder.push_key_mut(k);
1216                self.kv.from_refs(key, val);
1217            } else if v != val {
1218                self.builder.push_val_mut(v);
1219                val.clone_to(v);
1220            }
1221        } else {
1222            self.has_kv = true;
1223            self.kv.from_refs(key, val);
1224        }
1225        self.builder.push_time_diff(time, weight);
1226    }
1227
1228    /// Adds tuple `(key, val, time, weight)` to the batch.
1229    pub fn push_vals(
1230        &mut self,
1231        key: &mut Output::Key,
1232        val: &mut Output::Val,
1233        time: &mut Output::Time,
1234        weight: &mut Output::R,
1235    ) {
1236        if self.has_kv {
1237            let (k, v) = self.kv.split_mut();
1238            if k != key {
1239                self.builder.push_val_mut(v);
1240                self.builder.push_key_mut(k);
1241                self.kv.from_vals(key, val);
1242            } else if v != val {
1243                self.builder.push_val_mut(v);
1244                val.move_to(v);
1245            }
1246        } else {
1247            self.has_kv = true;
1248            self.kv.from_vals(key, val);
1249        }
1250        self.builder.push_time_diff_mut(time, weight);
1251    }
1252
1253    pub fn reserve(&mut self, additional: usize) {
1254        self.builder.reserve(additional)
1255    }
1256
1257    /// Adds all of the tuples in `iter` to the batch.
1258    pub fn extend<'a, I>(&mut self, iter: I)
1259    where
1260        Output::Time: PartialEq<()>,
1261        I: Iterator<Item = &'a mut WeightedItem<Output::Key, Output::Val, Output::R>>,
1262    {
1263        let (lower, upper) = iter.size_hint();
1264        self.reserve(upper.unwrap_or(lower));
1265
1266        for item in iter {
1267            let (kv, w) = item.split_mut();
1268            let (k, v) = kv.split_mut();
1269
1270            self.push_vals(k, v, &mut Output::Time::default(), w);
1271        }
1272    }
1273
1274    /// Completes building and returns the batch.
1275    pub fn done(mut self) -> Output {
1276        if self.has_kv {
1277            let (k, v) = self.kv.split_mut();
1278            self.builder.push_val_mut(v);
1279            self.builder.push_key_mut(k);
1280        }
1281        self.builder.done()
1282    }
1283}
1284
1285/// Merges all of the batches in `batches`, applying `key_filter` and
1286/// `value_filter`, and returns the merged result.
1287///
1288/// The filters won't be applied to batches that don't get merged at all, that
1289/// is, if `batches` contains only one non-empty batch, or if it contains two
1290/// small batches that merge to become an empty batch alongside a third larger
1291/// batch, etc.
1292pub fn merge_batches<B, T>(
1293    factories: &B::Factories,
1294    batches: T,
1295    key_filter: &Option<Filter<B::Key>>,
1296    value_filter: &Option<GroupFilter<B::Val>>,
1297) -> B
1298where
1299    T: IntoIterator<Item = B>,
1300    B: Batch,
1301{
1302    // Collect input batches, discarding empty batches.
1303    let mut batches = batches
1304        .into_iter()
1305        .filter(|b| !b.is_empty())
1306        .collect::<Vec<_>>();
1307
1308    // Merge groups of up to 64 input batches to one output batch each.
1309    //
1310    // In practice, there are <= 64 input batches and 1 output batch (or 0 if
1311    // the inputs cancel each other out).
1312    while batches.len() > 1 {
1313        let mut inputs = batches.split_off(batches.len().saturating_sub(64));
1314        let result: B = ListMerger::merge(
1315            factories,
1316            B::Builder::for_merge(factories, &inputs, Some(BatchLocation::Memory)),
1317            inputs
1318                .iter_mut()
1319                .map(|b| b.consuming_cursor(key_filter.clone(), value_filter.clone()))
1320                .collect(),
1321        );
1322        if !result.is_empty() {
1323            batches.push(result);
1324        }
1325    }
1326
1327    // Take the final output batch, or synthesize an empty one if all the
1328    // batches added up to nothing.
1329    batches.pop().unwrap_or_else(|| B::dyn_empty(factories))
1330}
1331
1332/// Merges all of the batches in `batches`, applying `key_filter` and
1333/// `value_filter`, and returns the merged result.
1334///
1335/// Every tuple will be passed through the filters.
1336pub fn merge_batches_by_reference<'a, B, T>(
1337    factories: &B::Factories,
1338    batches: T,
1339    key_filter: &Option<Filter<B::Key>>,
1340    value_filter: &Option<GroupFilter<B::Val>>,
1341) -> B
1342where
1343    T: IntoIterator<Item = &'a B>,
1344    B: Batch,
1345{
1346    // Collect input batches, discarding empty batches.
1347    let mut batches = batches
1348        .into_iter()
1349        .filter(|b| !b.is_empty())
1350        .collect::<Vec<_>>();
1351
1352    // Merge groups of up to 64 input batches to one output batch each. This
1353    // also transforms `&B` in `batches` into `B` in `outputs`.
1354    //
1355    // In practice, there are <= 64 input batches and 1 output batch (or 0 if
1356    // the inputs cancel each other out).
1357    let mut outputs = Vec::with_capacity(batches.len().div_ceil(64));
1358    while !batches.is_empty() {
1359        let inputs = batches.split_off(batches.len().saturating_sub(64));
1360        let result: B = ListMerger::merge(
1361            factories,
1362            B::Builder::for_merge(
1363                factories,
1364                inputs.iter().cloned(),
1365                Some(BatchLocation::Memory),
1366            ),
1367            inputs
1368                .into_iter()
1369                .map(|b| b.merge_cursor(key_filter.clone(), value_filter.clone()))
1370                .collect(),
1371        );
1372        if !result.is_empty() {
1373            outputs.push(result);
1374        }
1375    }
1376
1377    // Merge the output batches (in practice, either 0 or 1 of them).
1378    merge_batches(factories, outputs, key_filter, value_filter)
1379}
1380
1381/// Compares two batches for equality.  This works regardless of whether the
1382/// batches are the same type, as long as their key, value, and weight types can
1383/// be compared for equality.
1384///
1385/// This can't be implemented as `PartialEq` because that is specialized for
1386/// comparing particular batch types (often in faster ways than this generic
1387/// function).  This function is mainly useful for testing in any case.
1388pub fn eq_batch<A, B, KA, VA, RA, KB, VB, RB>(a: &A, b: &B) -> bool
1389where
1390    A: BatchReader<Key = KA, Val = VA, Time = (), R = RA>,
1391    B: BatchReader<Key = KB, Val = VB, Time = (), R = RB>,
1392    KA: PartialEq<KB> + ?Sized,
1393    VA: PartialEq<VB> + ?Sized,
1394    RA: PartialEq<RB> + ?Sized,
1395    KB: ?Sized,
1396    VB: ?Sized,
1397    RB: ?Sized,
1398{
1399    let mut c1 = a.cursor();
1400    let mut c2 = b.cursor();
1401    while c1.key_valid() && c2.key_valid() {
1402        if c1.key() != c2.key() {
1403            return false;
1404        }
1405        while c1.val_valid() && c2.val_valid() {
1406            if c1.val() != c2.val() || c1.weight() != c2.weight() {
1407                return false;
1408            }
1409            c1.step_val();
1410            c2.step_val();
1411        }
1412        if c1.val_valid() || c2.val_valid() {
1413            return false;
1414        }
1415        c1.step_key();
1416        c2.step_key();
1417    }
1418    !c1.key_valid() && !c2.key_valid()
1419}
1420
1421fn serialize_wset<B, K, R>(batch: &B) -> Vec<u8>
1422where
1423    B: BatchReader<Key = K, Val = DynUnit, Time = (), R = R>,
1424    K: DataTrait + ?Sized,
1425    R: WeightTrait + ?Sized,
1426{
1427    SerializerInner::to_fbuf_with_thread_local(|s| {
1428        let mut offsets = Vec::with_capacity(2 * batch.len());
1429        let mut cursor = batch.cursor();
1430        while cursor.key_valid() {
1431            offsets.push(cursor.key().serialize(s)?);
1432            offsets.push(cursor.weight().serialize(s)?);
1433            cursor.step_key();
1434        }
1435        s.serialize_value(&offsets)
1436    })
1437    .into_vec()
1438}
1439
1440fn deserialize_wset<B, K, R>(factories: &B::Factories, data: &[u8]) -> B
1441where
1442    B: Batch<Key = K, Val = DynUnit, Time = (), R = R>,
1443    K: DataTrait + ?Sized,
1444    R: WeightTrait + ?Sized,
1445{
1446    let offsets = unsafe { archived_root::<Vec<usize>>(data) };
1447    assert!(offsets.len() % 2 == 0);
1448    let n = offsets.len() / 2;
1449    let mut builder = B::Builder::with_capacity(factories, n, n);
1450    let mut key = factories.key_factory().default_box();
1451    let mut diff = factories.weight_factory().default_box();
1452    for i in 0..n {
1453        unsafe { key.deserialize_from_bytes(data, offsets[i * 2] as usize) };
1454        unsafe { diff.deserialize_from_bytes(data, offsets[i * 2 + 1] as usize) };
1455        builder.push_val_diff(&(), &diff);
1456        builder.push_key(&key);
1457    }
1458    builder.done()
1459}
1460
1461/// Separator that identifies the end of values for a key.
1462const SEPARATOR: u64 = u64::MAX;
1463
1464#[cfg(debug_assertions)]
1465#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1466enum State {
1467    Key,
1468    Val,
1469    Diff,
1470}
1471
1472pub struct IndexedWSetSerializer {
1473    fbuf: FBuf,
1474    offsets: Vec<usize>,
1475    n_keys: usize,
1476    n_values: usize,
1477    #[cfg(debug_assertions)]
1478    state: State,
1479}
1480
1481impl IndexedWSetSerializer {
1482    pub fn with_capacity(estimated_keys: usize, estimated_values: usize) -> Self {
1483        let mut offsets = Vec::with_capacity(2 + 2 * estimated_keys + 2 * estimated_values);
1484        offsets.push(0);
1485        offsets.push(0);
1486        Self {
1487            fbuf: FBuf::new(),
1488            offsets,
1489            n_keys: 0,
1490            n_values: 0,
1491            #[cfg(debug_assertions)]
1492            state: State::Key,
1493        }
1494    }
1495
1496    pub fn push_diff<R: WeightTrait + ?Sized>(
1497        &mut self,
1498        weight: &R,
1499        serializer_inner: &mut SerializerInner,
1500    ) {
1501        #[cfg(debug_assertions)]
1502        {
1503            debug_assert_ne!(self.state, State::Diff);
1504            self.state = State::Diff;
1505        }
1506
1507        serializer_inner.with(FBufSerializer::new(&mut self.fbuf), |s| {
1508            self.offsets.push(weight.serialize(s).unwrap())
1509        });
1510    }
1511
1512    pub fn push_val<V: DataTrait + ?Sized>(
1513        &mut self,
1514        val: &V,
1515        serializer_inner: &mut SerializerInner,
1516    ) {
1517        #[cfg(debug_assertions)]
1518        {
1519            debug_assert_eq!(self.state, State::Diff);
1520            self.state = State::Val;
1521        }
1522
1523        self.n_values += 1;
1524        serializer_inner.with(FBufSerializer::new(&mut self.fbuf), |s| {
1525            self.offsets.push(val.serialize(s).unwrap())
1526        });
1527    }
1528
1529    pub fn push_key<K: DataTrait + ?Sized>(
1530        &mut self,
1531        key: &K,
1532        serializer_inner: &mut SerializerInner,
1533    ) {
1534        #[cfg(debug_assertions)]
1535        {
1536            debug_assert_eq!(self.state, State::Val);
1537            self.state = State::Key;
1538        }
1539
1540        self.offsets.push(SEPARATOR as usize);
1541        self.n_keys += 1;
1542        serializer_inner.with(FBufSerializer::new(&mut self.fbuf), |s| {
1543            self.offsets.push(key.serialize(s).unwrap())
1544        });
1545    }
1546
1547    pub fn done(mut self, serializer_inner: &mut SerializerInner) -> FBuf {
1548        #[cfg(debug_assertions)]
1549        debug_assert_eq!(self.state, State::Key);
1550        self.offsets[0] = self.n_keys;
1551        self.offsets[1] = self.n_values;
1552        serializer_inner.with(FBufSerializer::new(&mut self.fbuf), |s| {
1553            s.serialize_value(&self.offsets).unwrap()
1554        });
1555        self.fbuf
1556    }
1557}
1558
1559pub fn serialize_indexed_wset<B, K, V, R>(batch: &B, serializer_inner: &mut SerializerInner) -> FBuf
1560where
1561    B: BatchReader<Key = K, Val = V, Time = (), R = R>,
1562    K: DataTrait + ?Sized,
1563    V: DataTrait + ?Sized,
1564    R: WeightTrait + ?Sized,
1565{
1566    let mut serializer = IndexedWSetSerializer::with_capacity(batch.key_count(), batch.len());
1567    let mut cursor = batch.cursor();
1568
1569    while cursor.key_valid() {
1570        while cursor.val_valid() {
1571            serializer.push_diff(cursor.weight(), serializer_inner);
1572            serializer.push_val(cursor.val(), serializer_inner);
1573            cursor.step_val();
1574        }
1575        serializer.push_key(cursor.key(), serializer_inner);
1576        cursor.step_key();
1577    }
1578    serializer.done(serializer_inner)
1579}
1580
1581pub fn deserialize_indexed_wset<B, K, V, R>(factories: &B::Factories, data: &[u8]) -> B
1582where
1583    B: Batch<Key = K, Val = V, Time = (), R = R>,
1584    K: DataTrait + ?Sized,
1585    V: DataTrait + ?Sized,
1586    R: WeightTrait + ?Sized,
1587{
1588    let offsets = unsafe { archived_root::<Vec<usize>>(data) };
1589    let n_keys = offsets[0] as usize;
1590    let n_values = offsets[1] as usize;
1591
1592    let mut builder = B::Builder::with_capacity(factories, n_keys, n_values);
1593    let mut key = factories.key_factory().default_box();
1594    let mut val = factories.val_factory().default_box();
1595    let mut diff = factories.weight_factory().default_box();
1596
1597    let mut current_offset = 2;
1598
1599    while current_offset < offsets.len() {
1600        while offsets[current_offset] != SEPARATOR {
1601            unsafe { diff.deserialize_from_bytes(data, offsets[current_offset] as usize) };
1602            current_offset += 1;
1603            unsafe { val.deserialize_from_bytes(data, offsets[current_offset] as usize) };
1604            current_offset += 1;
1605
1606            builder.push_val_diff(&val, &diff);
1607        }
1608        current_offset += 1;
1609
1610        unsafe { key.deserialize_from_bytes(data, offsets[current_offset] as usize) };
1611        current_offset += 1;
1612
1613        builder.push_key(&key);
1614    }
1615    builder.done()
1616}
1617
1618#[cfg(test)]
1619mod serialize_test {
1620    use crate::{
1621        DynZWeight, OrdIndexedZSet,
1622        algebra::OrdIndexedZSet as DynOrdIndexedZSet,
1623        dynamic::DynData,
1624        indexed_zset,
1625        storage::file::SerializerInner,
1626        trace::{BatchReader, deserialize_indexed_wset, serialize_indexed_wset},
1627    };
1628
1629    #[test]
1630    fn test_serialize_indexed_wset() {
1631        let test1: OrdIndexedZSet<u64, u64> = indexed_zset! {};
1632        let test2 = indexed_zset! { 1 => { 1 => 1 } };
1633        let test3 =
1634            indexed_zset! { 1 => { 1 => 1, 2 => 2, 3 => 3 }, 2 => { 1 => 1, 2 => 2, 3 => 3 } };
1635
1636        for test in [test1, test2, test3] {
1637            let serialized = serialize_indexed_wset(&*test, &mut SerializerInner::new());
1638            let deserialized = deserialize_indexed_wset::<
1639                DynOrdIndexedZSet<DynData, DynData>,
1640                DynData,
1641                DynData,
1642                DynZWeight,
1643            >(&test.factories(), &serialized);
1644
1645            assert_eq!(&*test, &deserialized);
1646        }
1647    }
1648
1649    #[test]
1650    fn test_serialize_indexed_wset_tup0_key() {
1651        let test1: OrdIndexedZSet<(), u64> = indexed_zset! {};
1652        let test2 = indexed_zset! { () => { 1 => 1 } };
1653
1654        for test in [test1, test2] {
1655            let serialized = serialize_indexed_wset(&*test, &mut SerializerInner::new());
1656            let deserialized = deserialize_indexed_wset::<
1657                DynOrdIndexedZSet<DynData, DynData>,
1658                DynData,
1659                DynData,
1660                DynZWeight,
1661            >(&test.factories(), &serialized);
1662
1663            assert_eq!(&*test, &deserialized);
1664        }
1665    }
1666
1667    #[test]
1668    fn test_serialize_indexed_wset_tup0_val() {
1669        let test1: OrdIndexedZSet<u64, ()> = indexed_zset! {};
1670        let test2 = indexed_zset! { 1 => { () => 1 } };
1671        let test3 = indexed_zset! { 1 => { () => 1 }, 2 => { () => 1 } };
1672
1673        for test in [test1, test2, test3] {
1674            let serialized = serialize_indexed_wset(&*test, &mut SerializerInner::new());
1675            let deserialized = deserialize_indexed_wset::<
1676                DynOrdIndexedZSet<DynData, DynData>,
1677                DynData,
1678                DynData,
1679                DynZWeight,
1680            >(&test.factories(), &serialized);
1681
1682            assert_eq!(&*test, &deserialized);
1683        }
1684    }
1685}