1use crate::{
2 Circuit, DBData, DynZWeight, NumEntries, Runtime, Stream, ZWeight,
3 algebra::{
4 IndexedZSet, OrdIndexedZSet, OrdIndexedZSetFactories, OrdZSet, OrdZSetFactories, ZSet,
5 },
6 circuit::{RootCircuit, checkpointer::Checkpoint},
7 dynamic::{
8 ClonableTrait, DataTrait, DynBool, DynData, DynOpt, DynPair, DynPairs, DynUnit,
9 DynWeightedPairs, Erase, Factory, LeanVec, WithFactory,
10 },
11 operator::{
12 Input, InputHandle, Update,
13 dynamic::{
14 input_upsert::{
15 DynUpdate, InputUpsertFactories, InputUpsertWithWaterlineFactories, PatchFunc,
16 },
17 time_series::LeastUpperBoundFunc,
18 upsert::UpdateSetFactories,
19 },
20 },
21 trace::{Batch, BatchFactories, BatchReaderFactories, Batcher, FallbackWSet, Rkyv},
22 utils::Tup2,
23};
24use std::{
25 mem::{replace, swap},
26 ops::Not,
27 panic::Location,
28 sync::{
29 Arc,
30 atomic::{AtomicUsize, Ordering},
31 },
32};
33
34pub type IndexedZSetStream<K, V> = Stream<RootCircuit, OrdIndexedZSet<K, V>>;
35pub type ZSetStream<K> = Stream<RootCircuit, OrdZSet<K>>;
36
37pub struct AddInputZSetFactories<K: DataTrait + ?Sized> {
38 zset_factories: OrdZSetFactories<K>,
39 weighted_pairs_factory: &'static dyn Factory<DynWeightedPairs<DynPair<K, DynUnit>, DynZWeight>>,
40 pairs_factory: &'static dyn Factory<DynPairs<DynPair<K, DynUnit>, DynZWeight>>,
41 pair_factory: &'static dyn Factory<DynPair<DynPair<K, DynUnit>, DynZWeight>>,
42}
43
44impl<K> AddInputZSetFactories<K>
45where
46 K: DataTrait + ?Sized,
47{
48 pub fn new<KType>() -> Self
49 where
50 KType: DBData + Erase<K>,
51 {
52 Self {
53 zset_factories: BatchReaderFactories::new::<KType, (), ZWeight>(),
54 weighted_pairs_factory: WithFactory::<LeanVec<Tup2<Tup2<KType, ()>, ZWeight>>>::FACTORY,
55 pairs_factory: WithFactory::<LeanVec<Tup2<Tup2<KType, ()>, ZWeight>>>::FACTORY,
56 pair_factory: WithFactory::<Tup2<Tup2<KType, ()>, ZWeight>>::FACTORY,
57 }
58 }
59}
60
61impl<K> Clone for AddInputZSetFactories<K>
62where
63 K: DataTrait + ?Sized,
64{
65 fn clone(&self) -> Self {
66 Self {
67 zset_factories: self.zset_factories.clone(),
68 weighted_pairs_factory: self.weighted_pairs_factory,
69 pairs_factory: self.pairs_factory,
70 pair_factory: self.pair_factory,
71 }
72 }
73}
74
75pub struct AddInputIndexedZSetFactories<K, V>
76where
77 K: DataTrait + ?Sized,
78 V: DataTrait + ?Sized,
79{
80 indexed_zset_factories: OrdIndexedZSetFactories<K, V>,
81 pair_factory: &'static dyn Factory<DynPair<K, DynPair<V, DynZWeight>>>,
82 pairs_factory: &'static dyn Factory<DynPairs<K, DynPair<V, DynZWeight>>>,
83}
84
85impl<K, V> Clone for AddInputIndexedZSetFactories<K, V>
86where
87 K: DataTrait + ?Sized,
88 V: DataTrait + ?Sized,
89{
90 fn clone(&self) -> Self {
91 Self {
92 indexed_zset_factories: self.indexed_zset_factories.clone(),
93 pair_factory: self.pair_factory,
94 pairs_factory: self.pairs_factory,
95 }
96 }
97}
98
99impl<K, V> AddInputIndexedZSetFactories<K, V>
100where
101 K: DataTrait + ?Sized,
102 V: DataTrait + ?Sized,
103{
104 pub fn new<KType, VType>() -> Self
105 where
106 KType: DBData + Erase<K>,
107 VType: DBData + Erase<V>,
108 {
109 Self {
110 indexed_zset_factories: BatchReaderFactories::new::<KType, VType, ZWeight>(),
111 pairs_factory: WithFactory::<LeanVec<Tup2<KType, Tup2<VType, ZWeight>>>>::FACTORY,
112 pair_factory: WithFactory::<Tup2<KType, Tup2<VType, ZWeight>>>::FACTORY,
113 }
114 }
115}
116
117pub struct AddInputSetFactories<B>
118where
119 B: ZSet,
120{
121 update_set_factories: UpdateSetFactories<(), B>,
122 input_pair_factory: &'static dyn Factory<DynPair<B::Key, DynBool>>,
123 input_pairs_factory: &'static dyn Factory<DynPairs<B::Key, DynBool>>,
124 upsert_pair_factory: &'static dyn Factory<DynPair<B::Key, DynOpt<DynUnit>>>,
125 upsert_pairs_factory: &'static dyn Factory<DynPairs<B::Key, DynOpt<DynUnit>>>,
126}
127
128impl<B> Clone for AddInputSetFactories<B>
129where
130 B: ZSet,
131{
132 fn clone(&self) -> Self {
133 Self {
134 update_set_factories: self.update_set_factories.clone(),
135 input_pair_factory: self.input_pair_factory,
136 input_pairs_factory: self.input_pairs_factory,
137 upsert_pair_factory: self.upsert_pair_factory,
138 upsert_pairs_factory: self.upsert_pairs_factory,
139 }
140 }
141}
142
143impl<B> AddInputSetFactories<B>
144where
145 B: ZSet,
146{
147 pub fn new<KType>() -> Self
148 where
149 KType: DBData + Erase<B::Key>,
150 {
151 Self {
152 update_set_factories: UpdateSetFactories::new::<KType>(),
153 input_pair_factory: WithFactory::<Tup2<KType, bool>>::FACTORY,
154 input_pairs_factory: WithFactory::<LeanVec<Tup2<KType, bool>>>::FACTORY,
155 upsert_pair_factory: WithFactory::<Tup2<KType, Option<()>>>::FACTORY,
156 upsert_pairs_factory: WithFactory::<LeanVec<Tup2<KType, Option<()>>>>::FACTORY,
157 }
158 }
159}
160
161pub struct AddInputMapFactories<B, U>
162where
163 B: IndexedZSet,
164 U: DataTrait + ?Sized,
165{
166 upsert_factories: InputUpsertFactories<B, U>,
167 input_pair_factory: &'static dyn Factory<DynPair<B::Key, DynUpdate<B::Val, U>>>,
168 input_pairs_factory: &'static dyn Factory<DynPairs<B::Key, DynUpdate<B::Val, U>>>,
169 upsert_pair_factory: &'static dyn Factory<DynPair<B::Key, DynOpt<DynUnit>>>,
170}
171
172impl<B, U> AddInputMapFactories<B, U>
173where
174 B: IndexedZSet,
175 U: DataTrait + ?Sized,
176{
177 pub fn new<KType, VType, UType>() -> Self
178 where
179 KType: DBData + Erase<B::Key>,
180 VType: DBData + Erase<B::Val>,
181 UType: DBData + Erase<U>,
182 {
183 Self {
184 upsert_factories: InputUpsertFactories::new::<KType, VType, UType>(),
185 input_pair_factory: WithFactory::<Tup2<KType, Update<VType, UType>>>::FACTORY,
186 input_pairs_factory: WithFactory::<LeanVec<Tup2<KType, Update<VType, UType>>>>::FACTORY,
187 upsert_pair_factory: WithFactory::<Tup2<KType, Option<()>>>::FACTORY,
188 }
189 }
190}
191
192impl<B, U> Clone for AddInputMapFactories<B, U>
193where
194 B: IndexedZSet,
195 U: DataTrait + ?Sized,
196{
197 fn clone(&self) -> Self {
198 Self {
199 upsert_factories: self.upsert_factories.clone(),
200 input_pair_factory: self.input_pair_factory,
201 input_pairs_factory: self.input_pairs_factory,
202 upsert_pair_factory: self.upsert_pair_factory,
203 }
204 }
205}
206
207pub struct AddInputMapWithWaterlineFactories<B, U, E>
208where
209 B: IndexedZSet,
210 U: DataTrait + ?Sized,
211 E: DataTrait + ?Sized,
212{
213 upsert_factories: InputUpsertWithWaterlineFactories<B, U, E>,
214 input_pair_factory: &'static dyn Factory<DynPair<B::Key, DynUpdate<B::Val, U>>>,
215 input_pairs_factory: &'static dyn Factory<DynPairs<B::Key, DynUpdate<B::Val, U>>>,
216 upsert_pair_factory: &'static dyn Factory<DynPair<B::Key, DynOpt<DynUnit>>>,
217}
218
219impl<B, U, E> AddInputMapWithWaterlineFactories<B, U, E>
220where
221 B: IndexedZSet,
222 U: DataTrait + ?Sized,
223 E: DataTrait + ?Sized,
224{
225 pub fn new<KType, VType, UType, EType>() -> Self
226 where
227 KType: DBData + Erase<B::Key>,
228 VType: DBData + Erase<B::Val>,
229 UType: DBData + Erase<U>,
230 EType: DBData + Erase<E>,
231 {
232 Self {
233 upsert_factories: InputUpsertWithWaterlineFactories::new::<KType, VType, UType, EType>(
234 ),
235 input_pair_factory: WithFactory::<Tup2<KType, Update<VType, UType>>>::FACTORY,
236 input_pairs_factory: WithFactory::<LeanVec<Tup2<KType, Update<VType, UType>>>>::FACTORY,
237 upsert_pair_factory: WithFactory::<Tup2<KType, Option<()>>>::FACTORY,
238 }
239 }
240}
241
242impl<B, U, E> Clone for AddInputMapWithWaterlineFactories<B, U, E>
243where
244 B: IndexedZSet,
245 U: DataTrait + ?Sized,
246 E: DataTrait + ?Sized,
247{
248 fn clone(&self) -> Self {
249 Self {
250 upsert_factories: self.upsert_factories.clone(),
251 input_pair_factory: self.input_pair_factory,
252 input_pairs_factory: self.input_pairs_factory,
253 upsert_pair_factory: self.upsert_pair_factory,
254 }
255 }
256}
257
258impl RootCircuit {
259 pub fn dyn_add_input_zset_mono(
260 &self,
261 factories: &AddInputZSetFactories<DynData>,
262 ) -> (
263 ZSetStream<DynData>,
264 CollectionHandle<DynPair<DynData, DynUnit>, DynZWeight>,
265 ) {
266 self.dyn_add_input_zset(factories)
267 }
268
269 #[allow(clippy::type_complexity)]
270 pub fn dyn_add_input_indexed_zset_mono(
271 &self,
272 factories: &AddInputIndexedZSetFactories<DynData, DynData>,
273 ) -> (
274 IndexedZSetStream<DynData, DynData>,
275 CollectionHandle<DynData, DynPair<DynData, DynZWeight>>,
276 ) {
277 self.dyn_add_input_indexed_zset(factories)
278 }
279
280 pub fn dyn_add_input_set_mono(
281 &self,
282 persistent_id: Option<&str>,
283 factories: &AddInputSetFactories<OrdZSet<DynData>>,
284 ) -> (ZSetStream<DynData>, UpsertHandle<DynData, DynBool>) {
285 self.dyn_add_input_set(persistent_id, factories)
286 }
287
288 pub fn dyn_add_input_map_mono(
289 &self,
290 persistent_id: Option<&str>,
291 factories: &AddInputMapFactories<OrdIndexedZSet<DynData, DynData>, DynData>,
292 patch_func: PatchFunc<DynData, DynData>,
293 ) -> (
294 IndexedZSetStream<DynData, DynData>,
295 UpsertHandle<DynData, DynUpdate<DynData, DynData>>,
296 ) {
297 self.dyn_add_input_map(persistent_id, factories, patch_func)
298 }
299
300 #[allow(clippy::too_many_arguments)]
301 pub fn dyn_add_input_map_with_waterline_mono(
302 &self,
303 persistent_id: Option<&str>,
304 factories: &AddInputMapWithWaterlineFactories<
305 OrdIndexedZSet<DynData, DynData>,
306 DynData,
307 DynData,
308 >,
309 patch_func: PatchFunc<DynData, DynData>,
310 init_waterline: Box<dyn Fn() -> Box<DynData>>,
311 extract_ts: Box<dyn Fn(&DynData, &DynData, &mut DynData)>,
312 least_upper_bound: LeastUpperBoundFunc<DynData>,
313 filter_func: Box<dyn Fn(&DynData, &DynData, &DynData) -> bool>,
314 report_func: Box<dyn Fn(&DynData, &DynData, &DynData, ZWeight, &mut DynData)>,
315 ) -> (
316 IndexedZSetStream<DynData, DynData>,
317 Stream<RootCircuit, OrdZSet<DynData>>,
318 Stream<RootCircuit, Box<DynData>>,
319 UpsertHandle<DynData, DynUpdate<DynData, DynData>>,
320 ) {
321 self.dyn_add_input_map_with_waterline(
322 persistent_id,
323 factories,
324 patch_func,
325 init_waterline,
326 extract_ts,
327 least_upper_bound,
328 filter_func,
329 report_func,
330 )
331 }
332}
333
334impl RootCircuit {
335 #[track_caller]
352 pub fn dyn_add_input_zset<K>(
353 &self,
354 factories: &AddInputZSetFactories<K>,
355 ) -> (
356 ZSetStream<K>,
357 CollectionHandle<DynPair<K, DynUnit>, DynZWeight>,
358 )
359 where
360 K: DataTrait + ?Sized,
361 {
362 let pairs_factory = factories.pairs_factory;
363 let weighted_pairs_factory = factories.weighted_pairs_factory;
364
365 let zset_factories = factories.zset_factories.clone();
366
367 self.region("input_zset", || {
368 let (input, input_handle) = Input::new(
369 Location::caller(),
370 move |tuples: Vec<Box<DynPairs<_, _>>>| {
371 let mut pairs = weighted_pairs_factory.default_box();
372 let mut batcher =
373 <FallbackWSet<_, _> as Batch>::Batcher::new_batcher(&zset_factories, ());
374 for mut tuples in tuples {
375 pairs.from_pairs(tuples.as_mut());
376 batcher.push_batch(&mut pairs);
377 }
378 batcher.seal()
379 },
380 Arc::new(|| vec![pairs_factory.default_box()]),
381 );
382
383 let stream = self.add_source(input).dyn_shard(&factories.zset_factories);
394
395 let zset_handle = <CollectionHandle<DynPair<K, DynUnit>, DynZWeight>>::new(
396 factories.pair_factory,
397 factories.pairs_factory,
398 input_handle,
399 );
400
401 (stream, zset_handle)
402 })
403 }
404
405 #[allow(clippy::type_complexity)]
422 #[track_caller]
423 pub fn dyn_add_input_indexed_zset<K, V>(
424 &self,
425 factories: &AddInputIndexedZSetFactories<K, V>,
426 ) -> (
427 IndexedZSetStream<K, V>,
428 CollectionHandle<K, DynPair<V, DynZWeight>>,
429 )
430 where
431 K: DataTrait + ?Sized,
432 V: DataTrait + ?Sized,
433 {
434 let factories_clone = factories.clone();
435
436 let (input, input_handle) = Input::new(
437 Location::caller(),
438 move |tuples: Vec<Box<DynPairs<K, DynPair<V, DynZWeight>>>>| {
439 let mut indexed_tuples = factories_clone
440 .indexed_zset_factories
441 .weighted_items_factory()
442 .default_box();
443 let mut item = factories_clone
444 .indexed_zset_factories
445 .weighted_item_factory()
446 .default_box();
447
448 for mut tuples in tuples {
449 for kvw in tuples.dyn_iter_mut() {
450 let (k, vw) = kvw.split_mut();
451 let (v, w) = vw.split_mut();
452 let (kv, item_w) = item.split_mut();
453 let (item_k, item_v) = kv.split_mut();
454 k.clone_to(item_k);
455 v.clone_to(item_v);
456 w.clone_to(item_w);
457 indexed_tuples.push_val(&mut *item);
458 }
459 }
460 OrdIndexedZSet::dyn_from_tuples(
461 &factories_clone.indexed_zset_factories,
462 (),
463 &mut indexed_tuples,
464 )
465 },
466 Arc::new(|| vec![factories.pairs_factory.default_box()]),
467 );
468
469 let stream = self
476 .add_source(input)
477 .dyn_shard(&factories.indexed_zset_factories);
478
479 let zset_handle = <CollectionHandle<K, DynPair<V, DynZWeight>>>::new(
480 factories.pair_factory,
481 factories.pairs_factory,
482 input_handle,
483 );
484
485 (stream, zset_handle)
486 }
487
488 #[track_caller]
489 fn add_set_update<K, B>(
490 &self,
491 persistent_id: Option<&str>,
492 factories: &AddInputSetFactories<B>,
493 input_stream: Stream<Self, Vec<Box<DynPairs<K, DynBool>>>>,
494 ) -> Stream<Self, B>
495 where
496 K: DataTrait + ?Sized,
497 B: ZSet<Key = K>,
498 {
499 let factories_clone = factories.clone();
500
501 let sorted = input_stream
502 .apply_owned(move |upserts| {
503 struct UpsertPosition<T> {
504 upserts: T,
505 position: usize,
506 }
507 let mut upserts = upserts
510 .into_iter()
511 .filter_map(|mut upserts| {
512 upserts.sort_by_key();
513
514 upserts.dedup_by_key_keep_last();
516
517 upserts.is_empty().not().then(|| UpsertPosition {
518 upserts,
519 position: 0,
520 })
521 })
522 .collect::<Vec<_>>();
523
524 let mut result = factories_clone.upsert_pairs_factory.default_box();
525 let mut tuple = factories_clone.upsert_pair_factory.default_box();
526
527 while !upserts.is_empty() {
528 let min_index = (0..upserts.len())
529 .min_by(|a, b| {
530 let a = upserts[*a].upserts.index(upserts[*a].position);
531 let b = upserts[*b].upserts.index(upserts[*b].position);
532 a.cmp(b)
533 })
534 .unwrap();
535 let min = &mut upserts[min_index];
536 let upsert = min.upserts.index_mut(min.position);
537
538 let (k, v) = upsert.split_mut();
539 let mut v = if **v { Some(()) } else { None };
540 tuple.from_vals(k, v.erase_mut());
541 result.push_val(&mut *tuple);
542
543 min.position += 1;
544 if min.position >= min.upserts.len() {
545 upserts.remove(min_index);
546 }
547 }
548
549 result
550 })
551 .mark_sharded();
553
554 sorted.update_set::<B>(persistent_id, &factories.update_set_factories)
555 }
556
557 #[track_caller]
558 fn add_upsert_indexed<K, V, U, B>(
559 &self,
560 persistent_id: Option<&str>,
561 factories: &AddInputMapFactories<B, U>,
562 input_stream: Stream<Self, Vec<Box<DynPairs<K, DynUpdate<V, U>>>>>,
563 patch_func: PatchFunc<V, U>,
564 ) -> Stream<Self, B>
565 where
566 B: IndexedZSet<Key = K, Val = V>,
567 K: DataTrait + ?Sized,
568 V: DataTrait + ?Sized,
569 U: DataTrait + ?Sized,
570 {
571 let sorted = input_stream.apply_owned(move |mut upserts| {
572 upserts.retain_mut(|pairs| {
575 pairs.sort_by_key();
576 !pairs.is_empty()
577 });
578
579 upserts
580 });
581
582 if Runtime::runtime().is_none_or(|rt| rt.layout().is_solo()) {
583 sorted.mark_sharded();
585 }
586 sorted.input_upsert::<B>(persistent_id, &factories.upsert_factories, patch_func)
587 }
588
589 #[allow(clippy::too_many_arguments)]
590 #[track_caller]
591 fn add_upsert_indexed_with_waterline<K, V, U, B, W, E>(
592 &self,
593 persistent_id: Option<&str>,
594 factories: &AddInputMapWithWaterlineFactories<B, U, E>,
595 input_stream: Stream<Self, Vec<Box<DynPairs<K, DynUpdate<V, U>>>>>,
596 patch_func: PatchFunc<V, U>,
597 init_waterline: Box<dyn Fn() -> Box<W>>,
598 extract_ts: Box<dyn Fn(&B::Key, &B::Val, &mut W)>,
599 least_upper_bound: LeastUpperBoundFunc<W>,
600 filter_func: Box<dyn Fn(&W, &B::Key, &B::Val) -> bool>,
601 report_func: Box<dyn Fn(&W, &B::Key, &B::Val, ZWeight, &mut E)>,
602 ) -> (
603 Stream<Self, B>,
604 Stream<Self, OrdZSet<E>>,
605 Stream<Self, Box<W>>,
606 )
607 where
608 B: IndexedZSet<Key = K, Val = V>,
609 K: DataTrait + ?Sized,
610 V: DataTrait + ?Sized,
611 U: DataTrait + ?Sized,
612 W: DataTrait + Checkpoint + ?Sized,
613 E: DataTrait + ?Sized,
614 Box<W>: Checkpoint + Clone + NumEntries + Rkyv,
615 {
616 let sorted = input_stream.apply_owned(move |mut upserts| {
617 upserts.retain_mut(|pairs| {
620 pairs.sort_by_key();
621 !pairs.is_empty()
622 });
623
624 upserts
625 });
626
627 if Runtime::runtime().is_none_or(|rt| rt.layout().is_solo()) {
628 sorted.mark_sharded();
630 }
631
632 sorted.input_upsert_with_waterline::<B, W, E>(
633 persistent_id,
634 &factories.upsert_factories,
635 patch_func,
636 init_waterline,
637 extract_ts,
638 least_upper_bound,
639 filter_func,
640 report_func,
641 )
642 }
643
644 #[track_caller]
691 pub fn dyn_add_input_set<K>(
692 &self,
693 persistent_id: Option<&str>,
694 factories: &AddInputSetFactories<OrdZSet<K>>,
695 ) -> (ZSetStream<K>, UpsertHandle<K, DynBool>)
696 where
697 K: DataTrait + ?Sized,
698 {
699 self.region("input_set", || {
700 let (input, input_handle) = Input::new(
701 Location::caller(),
702 |tuples: Vec<Box<DynPairs<K, DynBool>>>| tuples,
703 Arc::new(|| vec![factories.input_pairs_factory.default_box()]),
704 );
705 let input_stream = self.add_source(input);
706 let upsert_handle = <UpsertHandle<K, DynBool>>::new(
707 factories.input_pair_factory,
708 factories.input_pairs_factory,
709 input_handle,
710 );
711
712 let upsert: Stream<RootCircuit, OrdZSet<K>> =
713 self.add_set_update(persistent_id, factories, input_stream);
714
715 (upsert, upsert_handle)
716 })
717 }
718
719 #[track_caller]
781 pub fn dyn_add_input_map<K, V, U>(
782 &self,
783 persistent_id: Option<&str>,
784 factories: &AddInputMapFactories<OrdIndexedZSet<K, V>, U>,
785 patch_func: PatchFunc<V, U>,
786 ) -> (IndexedZSetStream<K, V>, UpsertHandle<K, DynUpdate<V, U>>)
787 where
788 K: DataTrait + ?Sized,
789 V: DataTrait + ?Sized,
790 U: DataTrait + ?Sized,
791 {
792 self.region("input_map", || {
793 let (input, input_handle) = Input::new(
794 Location::caller(),
795 |tuples: Vec<Box<DynPairs<K, DynUpdate<V, U>>>>| tuples,
796 Arc::new(|| vec![factories.input_pairs_factory.default_box()]),
797 );
798 let input_stream = self.add_source(input);
799 let zset_handle = <UpsertHandle<K, DynUpdate<V, U>>>::new(
800 factories.input_pair_factory,
801 factories.input_pairs_factory,
802 input_handle,
803 );
804
805 let upsert =
806 self.add_upsert_indexed(persistent_id, factories, input_stream, patch_func);
807
808 (upsert, zset_handle)
809 })
810 }
811
812 #[allow(clippy::too_many_arguments)]
813 #[track_caller]
814 pub fn dyn_add_input_map_with_waterline<K, V, U, W, E>(
815 &self,
816 persistent_id: Option<&str>,
817 factories: &AddInputMapWithWaterlineFactories<OrdIndexedZSet<K, V>, U, E>,
818 patch_func: PatchFunc<V, U>,
819 init_waterline: Box<dyn Fn() -> Box<W>>,
820 extract_ts: Box<dyn Fn(&K, &V, &mut W)>,
821 least_upper_bound: LeastUpperBoundFunc<W>,
822 filter_func: Box<dyn Fn(&W, &K, &V) -> bool>,
823 report_func: Box<dyn Fn(&W, &K, &V, ZWeight, &mut E)>,
824 ) -> (
825 IndexedZSetStream<K, V>,
826 Stream<RootCircuit, OrdZSet<E>>,
827 Stream<RootCircuit, Box<W>>,
828 UpsertHandle<K, DynUpdate<V, U>>,
829 )
830 where
831 K: DataTrait + ?Sized,
832 V: DataTrait + ?Sized,
833 U: DataTrait + ?Sized,
834 W: DataTrait + Checkpoint + ?Sized,
835 E: DataTrait + ?Sized,
836 Box<W>: Checkpoint + Clone + NumEntries + Rkyv,
837 {
838 self.region("input_map_with_waterline", || {
839 let (input, input_handle) = Input::new(
840 Location::caller(),
841 |tuples: Vec<Box<DynPairs<K, DynUpdate<V, U>>>>| tuples,
842 Arc::new(|| vec![factories.input_pairs_factory.default_box()]),
843 );
844 let input_stream = self.add_source(input);
845 let zset_handle = <UpsertHandle<K, DynUpdate<V, U>>>::new(
846 factories.input_pair_factory,
847 factories.input_pairs_factory,
848 input_handle,
849 );
850
851 let (upsert, errors, waterline) = self.add_upsert_indexed_with_waterline(
852 persistent_id,
853 factories,
854 input_stream,
855 patch_func,
856 init_waterline,
857 extract_ts,
858 least_upper_bound,
859 filter_func,
860 report_func,
861 );
862
863 (upsert, errors, waterline, zset_handle)
864 })
865 }
866}
867
868pub struct CollectionHandle<K: DataTrait + ?Sized, V: DataTrait + ?Sized> {
941 pair_factory: &'static dyn Factory<DynPair<K, V>>,
942 pub pairs_factory: &'static dyn Factory<DynPairs<K, V>>,
943 pub input_handle: InputHandle<Vec<Box<DynPairs<K, V>>>>,
944 next_worker: Arc<AtomicUsize>,
949}
950
951impl<K: DataTrait + ?Sized, V: DataTrait + ?Sized> Clone for CollectionHandle<K, V> {
952 fn clone(&self) -> Self {
953 Self {
954 pair_factory: self.pair_factory,
955 pairs_factory: self.pairs_factory,
956 input_handle: self.input_handle.clone(),
957 next_worker: self.next_worker.clone(),
958 }
959 }
960}
961
962impl<K: DataTrait + ?Sized, V: DataTrait + ?Sized> CollectionHandle<K, V> {
963 fn new(
964 pair_factory: &'static dyn Factory<DynPair<K, V>>,
965 pairs_factory: &'static dyn Factory<DynPairs<K, V>>,
966 input_handle: InputHandle<Vec<Box<DynPairs<K, V>>>>,
967 ) -> Self {
968 Self {
969 pair_factory,
970 pairs_factory,
971 input_handle,
972 next_worker: Arc::new(AtomicUsize::new(0)),
973 }
974 }
975
976 #[inline]
977 pub fn num_partitions(&self) -> usize {
978 self.input_handle.0.mailbox.len()
979 }
980
981 pub fn dyn_push(&self, k: &mut K, v: &mut V) {
983 let mut tuple = self.pair_factory.default_box();
984 let next_worker = match self.num_partitions() {
985 1 => 0,
986 n => self.next_worker.fetch_add(1, Ordering::AcqRel) % n,
987 };
988 self.input_handle.update_for_worker(next_worker, |tuples| {
989 tuple.from_vals(k, v);
990 tuples.first_mut().unwrap().push_val(&mut *tuple);
991 });
992 }
993
994 pub fn dyn_append(&self, vals: &mut Box<DynPairs<K, V>>) {
1015 let num_partitions = self.num_partitions();
1016 let next_worker = if num_partitions > 1 {
1017 self.next_worker.load(Ordering::Acquire)
1018 } else {
1019 0
1020 };
1021
1022 let quotient = vals.len() / num_partitions;
1026 let remainder = vals.len() % num_partitions;
1027 for i in 0..num_partitions {
1028 let mut partition_size = quotient;
1029 if i < remainder {
1030 partition_size += 1;
1031 }
1032
1033 let worker = (next_worker + i) % num_partitions;
1034 if partition_size == vals.len() {
1035 self.input_handle.update_for_worker(worker, |tuples| {
1036 let tuples = tuples.first_mut().unwrap();
1037 if tuples.is_empty() {
1038 swap(tuples, vals);
1039 } else {
1040 tuples.append(vals.as_vec_mut());
1041 }
1042 });
1043 break;
1044 }
1045
1046 self.input_handle.update_for_worker(worker, |tuples| {
1049 let tuples = tuples.first_mut().unwrap();
1050 let len = vals.len();
1051 tuples.append_range(vals.as_vec_mut(), len - partition_size, len);
1052 });
1053 vals.truncate(vals.len() - partition_size);
1054 }
1055
1056 assert_eq!(vals.len(), 0);
1057
1058 if remainder > 0 {
1062 self.next_worker
1063 .store(next_worker + remainder, Ordering::Release);
1064 }
1065 }
1066
1067 pub fn dyn_stage(
1076 &self,
1077 vals: &mut Box<DynPairs<K, V>>,
1078 next_worker: &mut usize,
1079 partitions: &mut [Box<DynPairs<K, V>>],
1080 ) {
1081 let num_partitions = self.num_partitions();
1082
1083 let quotient = vals.len() / num_partitions;
1087 let remainder = vals.len() % num_partitions;
1088 for i in 0..num_partitions {
1089 let mut partition_size = quotient;
1090 if i < remainder {
1091 partition_size += 1;
1092 }
1093
1094 let worker = (*next_worker + i) % num_partitions;
1095 let len = vals.len();
1096 if partition_size == len && partitions[worker].is_empty() {
1097 swap(&mut partitions[worker], vals);
1098 break;
1099 }
1100
1101 partitions[worker].append_range(vals.as_vec_mut(), len - partition_size, len);
1104 vals.truncate(len - partition_size);
1105 }
1106
1107 assert!(vals.is_empty());
1108
1109 *next_worker = (*next_worker + remainder) % num_partitions;
1113 }
1114
1115 pub fn dyn_append_staged(&self, vals: Vec<Box<DynPairs<K, V>>>) {
1127 for (worker, vals) in vals.into_iter().enumerate() {
1128 self.input_handle.update_for_worker(worker, |tuples| {
1129 tuples.push(vals);
1130 });
1131 }
1132 }
1133
1134 pub fn clear_input(&self) {
1151 self.input_handle.clear_for_all();
1152 }
1153}
1154
1155pub trait HashFunc<K: ?Sized>: Fn(&K) -> u32 + Send + Sync {}
1156
1157impl<K: ?Sized, F> HashFunc<K> for F where F: Fn(&K) -> u32 + Send + Sync {}
1158
1159pub struct UpsertHandle<K: DataTrait + ?Sized, V: DataTrait + ?Sized> {
1182 pair_factory: &'static dyn Factory<DynPair<K, V>>,
1183 pub pairs_factory: &'static dyn Factory<DynPairs<K, V>>,
1184 buffers: Vec<Box<DynPairs<K, V>>>,
1185 pub input_handle: InputHandle<Vec<Box<DynPairs<K, V>>>>,
1186 pub hash_func: Arc<dyn HashFunc<K>>,
1193}
1194
1195impl<K: DataTrait + ?Sized, V: DataTrait + ?Sized> Clone for UpsertHandle<K, V> {
1196 fn clone(&self) -> Self {
1197 Self::with_hasher(
1199 self.pair_factory,
1200 self.pairs_factory,
1201 self.input_handle.clone(),
1202 self.hash_func.clone(),
1203 )
1204 }
1205}
1206
1207impl<K: DataTrait + ?Sized, V: DataTrait + ?Sized> UpsertHandle<K, V> {
1208 fn new(
1209 pair_factory: &'static dyn Factory<DynPair<K, V>>,
1210 pairs_factory: &'static dyn Factory<DynPairs<K, V>>,
1211 input_handle: InputHandle<Vec<Box<DynPairs<K, V>>>>,
1212 ) -> Self {
1213 Self::with_hasher(
1214 pair_factory,
1215 pairs_factory,
1216 input_handle,
1217 Arc::new(|k: &K| k.default_hash() as u32) as Arc<dyn HashFunc<K>>,
1218 )
1219 }
1220
1221 fn with_hasher(
1222 pair_factory: &'static dyn Factory<DynPair<K, V>>,
1223 pairs_factory: &'static dyn Factory<DynPairs<K, V>>,
1224 input_handle: InputHandle<Vec<Box<DynPairs<K, V>>>>,
1225 hash_func: Arc<dyn HashFunc<K>>,
1226 ) -> Self {
1227 Self {
1228 pair_factory,
1229 pairs_factory,
1230 buffers: vec![pairs_factory.default_box(); input_handle.0.mailbox.len()],
1231 input_handle,
1232 hash_func,
1233 }
1234 }
1235
1236 #[inline]
1237 pub fn num_partitions(&self) -> usize {
1238 self.input_handle.0.mailbox.len()
1239 }
1240
1241 pub fn dyn_push(&self, k: &mut K, v: &mut V) {
1243 let num_partitions = self.num_partitions();
1244
1245 if num_partitions > 1 {
1246 self.input_handle.update_for_worker(
1247 ((self.hash_func)(k) as usize) % num_partitions,
1248 |tuples| {
1249 let mut tuple = self.pair_factory.default_box();
1250 tuple.from_vals(k, v);
1251 tuples.first_mut().unwrap().push_val(&mut *tuple);
1252 },
1253 );
1254 } else {
1255 self.input_handle.update_for_worker(0, |tuples| {
1256 let mut tuple = self.pair_factory.default_box();
1257 tuple.from_vals(k, v);
1258 tuples.first_mut().unwrap().push_val(&mut *tuple);
1259 });
1260 }
1261 }
1262
1263 pub fn dyn_append(&mut self, vals: &mut Box<DynPairs<K, V>>) {
1284 let num_partitions = self.num_partitions();
1285
1286 if num_partitions > 1 {
1287 for kv in vals.dyn_iter_mut() {
1288 let k = kv.fst();
1289 self.buffers[((self.hash_func)(k) as usize) % num_partitions].push_val(kv)
1290 }
1291 vals.clear();
1292 for worker in 0..num_partitions {
1293 self.input_handle.update_for_worker(worker, |tuples| {
1294 let tuples = tuples.first_mut().unwrap();
1295 if tuples.is_empty() {
1296 *tuples =
1297 replace(&mut self.buffers[worker], self.pairs_factory.default_box());
1298 } else {
1299 tuples.append(self.buffers[worker].as_vec_mut());
1300 }
1301 })
1302 }
1303 } else {
1304 self.input_handle.update_for_worker(0, |tuples| {
1305 let tuples = tuples.first_mut().unwrap();
1306 if tuples.is_empty() {
1307 *tuples = replace(vals, self.pairs_factory.default_box());
1308 } else {
1309 tuples.append(vals.as_vec_mut());
1310 }
1311 });
1312 }
1313 }
1314
1315 pub fn dyn_stage(
1324 &self,
1325 vals: &mut Box<DynPairs<K, V>>,
1326 partitions: &mut [Box<DynPairs<K, V>>],
1327 ) {
1328 let num_partitions = self.num_partitions();
1329
1330 for kv in vals.dyn_iter_mut() {
1331 let k = kv.fst();
1332 partitions[((self.hash_func)(k) as usize) % num_partitions].push_val(kv)
1333 }
1334 }
1335
1336 pub fn dyn_append_staged(&self, vals: Vec<Box<DynPairs<K, V>>>) {
1348 for (worker, vals) in vals.into_iter().enumerate() {
1349 self.input_handle.update_for_worker(worker, |tuples| {
1350 tuples.push(vals);
1351 });
1352 }
1353 }
1354
1355 pub fn clear_input(&self) {
1372 self.input_handle.clear_for_all();
1373 }
1374}
1375
1376#[cfg(test)]
1377mod test {
1378 use crate::{
1379 OutputHandle, RootCircuit, Runtime, Stream, ZWeight,
1380 dynamic::{DowncastTrait, DynData, Erase},
1381 indexed_zset,
1382 operator::{
1383 IndexedZSetHandle, MapHandle, SetHandle, StagedBuffers, Update, ZSetHandle,
1384 input::InputHandle,
1385 },
1386 trace::{BatchReaderFactories, Builder, Cursor},
1387 typed_batch::{
1388 BatchReader, DynBatch, DynBatchReader, DynOrdZSet, OrdIndexedZSet, OrdZSet, TypedBatch,
1389 TypedBox,
1390 },
1391 utils::Tup2,
1392 zset,
1393 };
1394 use anyhow::Result as AnyResult;
1395 use rand::seq::index;
1396 use rand::{Rng, SeedableRng};
1397 use rand_chacha::ChaCha8Rng;
1398 use std::{
1399 cmp::max,
1400 collections::{BTreeSet, HashMap, VecDeque},
1401 iter::once,
1402 ops::Mul,
1403 };
1404
1405 fn input_batches() -> Vec<OrdZSet<u64>> {
1406 vec![
1407 zset! { 1u64 => 1, 2 => 1, 3 => 1 },
1408 zset! { 5u64 => -1, 10 => 2, 11 => 11 },
1409 zset! {},
1410 ]
1411 }
1412
1413 fn input_vecs() -> Vec<Vec<Tup2<u64, ZWeight>>> {
1414 input_batches()
1415 .into_iter()
1416 .map(|batch| {
1417 let mut cursor = batch.cursor();
1418 let mut result = Vec::new();
1419
1420 while cursor.key_valid() {
1421 result.push(Tup2(
1422 *cursor.key().downcast_checked::<u64>(),
1423 *cursor.weight().downcast_checked::<ZWeight>(),
1424 ));
1425 cursor.step_key();
1426 }
1427 result
1428 })
1429 .collect()
1430 }
1431
1432 fn input_test_circuit(
1433 circuit: &RootCircuit,
1434 nworkers: usize,
1435 ) -> AnyResult<InputHandle<OrdZSet<u64>>> {
1436 let (stream, handle) = circuit.add_input_stream::<OrdZSet<u64>>();
1437
1438 let mut expected_batches = input_batches().into_iter().chain(input_batches()).chain(
1439 input_batches().into_iter().map(move |batch| {
1440 let mut cursor = batch.inner().cursor();
1442 let mut result = <DynOrdZSet<DynData> as DynBatch>::Builder::with_capacity(
1443 &BatchReaderFactories::new::<u64, (), ZWeight>(),
1444 batch.len(),
1445 batch.len(),
1446 );
1447
1448 while cursor.key_valid() {
1449 let w = cursor
1450 .weight()
1451 .downcast_checked::<ZWeight>()
1452 .mul(nworkers as i64);
1453 result.push_val_diff(().erase(), w.erase());
1454 result.push_key(cursor.key());
1455 cursor.step_key();
1456 }
1457 TypedBatch::new(result.done())
1458 }),
1459 );
1460
1461 stream.gather(0).inspect(move |batch| {
1462 if Runtime::worker_index() == 0 {
1463 assert_eq!(batch, &expected_batches.next().unwrap())
1464 }
1465 });
1466
1467 Ok(handle)
1468 }
1469
1470 #[test]
1471 fn input_test_st() {
1472 let (circuit, input_handle) =
1473 RootCircuit::build(move |circuit| input_test_circuit(circuit, 1)).unwrap();
1474
1475 for batch in input_batches().into_iter() {
1476 input_handle.set_for_worker(0, batch);
1477 circuit.transaction().unwrap();
1478 }
1479
1480 for batch in input_batches().into_iter() {
1481 input_handle.update_for_worker(0, |b| *b = batch);
1482 circuit.transaction().unwrap();
1483 }
1484
1485 for batch in input_batches().into_iter() {
1486 input_handle.set_for_all(batch);
1487 circuit.transaction().unwrap();
1488 }
1489 }
1490
1491 fn input_test_mt(workers: usize) {
1492 let (mut dbsp, input_handle) =
1493 Runtime::init_circuit(workers, move |circuit| input_test_circuit(circuit, workers))
1494 .unwrap();
1495
1496 for (round, batch) in input_batches().into_iter().enumerate() {
1497 input_handle.set_for_worker(round % workers, batch);
1498 dbsp.transaction().unwrap();
1499 }
1500
1501 for (round, batch) in input_batches().into_iter().enumerate() {
1502 input_handle.update_for_worker(round % workers, |b| *b = batch);
1503 dbsp.transaction().unwrap();
1504 }
1505
1506 for batch in input_batches().into_iter() {
1507 input_handle.set_for_all(batch);
1508 dbsp.transaction().unwrap();
1509 }
1510
1511 dbsp.kill().unwrap();
1512 }
1513
1514 #[test]
1515 fn input_test_mt1() {
1516 input_test_mt(1);
1517 }
1518
1519 #[test]
1520 fn input_test_mt4() {
1521 input_test_mt(4);
1522 }
1523
1524 fn zset_test_circuit(circuit: &RootCircuit) -> AnyResult<ZSetHandle<u64>> {
1525 let (stream, handle) = circuit.add_input_zset::<u64>();
1526
1527 let mut expected_batches = input_batches()
1528 .into_iter()
1529 .chain(input_batches())
1530 .chain(once(zset! {}));
1531 stream.gather(0).inspect(move |batch| {
1532 if Runtime::worker_index() == 0 {
1533 assert_eq!(batch, &expected_batches.next().unwrap())
1534 }
1535 });
1536
1537 Ok(handle)
1538 }
1539
1540 #[test]
1541 fn zset_test_st() {
1542 let (circuit, input_handle) =
1543 RootCircuit::build(move |circuit| zset_test_circuit(circuit)).unwrap();
1544
1545 for mut vec in input_vecs().into_iter() {
1546 input_handle.append(&mut vec);
1547 circuit.transaction().unwrap();
1548 }
1549
1550 for vec in input_vecs().into_iter() {
1551 for Tup2(k, w) in vec.into_iter() {
1552 input_handle.push(k, w);
1553 }
1554 input_handle.push(5, 1);
1555 input_handle.push(5, -1);
1556 circuit.transaction().unwrap();
1557 }
1558
1559 for mut vec in input_vecs().into_iter() {
1560 input_handle.append(&mut vec);
1561 }
1562 input_handle.clear_input();
1563 circuit.transaction().unwrap();
1564 }
1565
1566 fn zset_test_mt(workers: usize) {
1567 let (mut dbsp, input_handle) =
1568 Runtime::init_circuit(workers, |circuit| zset_test_circuit(circuit)).unwrap();
1569
1570 for mut vec in input_vecs().into_iter() {
1571 input_handle.append(&mut vec);
1572 dbsp.transaction().unwrap();
1573 }
1574
1575 for vec in input_vecs().into_iter() {
1576 for Tup2(k, w) in vec.into_iter() {
1577 input_handle.push(k, w);
1578 }
1579 input_handle.push(5, 1);
1580 input_handle.push(5, -1);
1581 dbsp.transaction().unwrap();
1582 }
1583
1584 for mut vec in input_vecs().into_iter() {
1585 input_handle.append(&mut vec);
1586 }
1587 input_handle.clear_input();
1588 dbsp.transaction().unwrap();
1589
1590 dbsp.kill().unwrap();
1591 }
1592
1593 #[test]
1594 fn zset_test_mt1() {
1595 zset_test_mt(1);
1596 }
1597
1598 #[test]
1599 fn zset_test_mt4() {
1600 zset_test_mt(4);
1601 }
1602
1603 fn input_indexed_batches() -> Vec<OrdIndexedZSet<u64, u64>> {
1604 vec![
1605 indexed_zset! { 1u64 => {1u64 => 1, 2 => 1}, 2 => { 3 => 1 }, 3 => {4 => -1, 5 => 5} },
1606 indexed_zset! { 5u64 => {10u64 => -1}, 10 => {2 => 1, 3 => -1}, 11 => {11 => 11} },
1607 indexed_zset! {},
1608 ]
1609 }
1610
1611 fn input_indexed_vecs() -> Vec<Vec<Tup2<u64, Tup2<u64, i64>>>> {
1612 input_indexed_batches()
1613 .into_iter()
1614 .map(|batch| {
1615 let mut cursor = batch.cursor();
1616 let mut result = Vec::new();
1617
1618 while cursor.key_valid() {
1619 while cursor.val_valid() {
1620 result.push(Tup2(
1621 *cursor.key().downcast_checked::<u64>(),
1622 Tup2(
1623 *cursor.val().downcast_checked::<u64>(),
1624 *cursor.weight().downcast_checked::<ZWeight>(),
1625 ),
1626 ));
1627 cursor.step_val();
1628 }
1629 cursor.step_key();
1630 }
1631 result
1632 })
1633 .collect()
1634 }
1635
1636 fn indexed_zset_test_circuit(circuit: &RootCircuit) -> AnyResult<IndexedZSetHandle<u64, u64>> {
1637 let (stream, handle) = circuit.add_input_indexed_zset::<u64, u64>();
1638
1639 let mut expected_batches = input_indexed_batches()
1640 .into_iter()
1641 .chain(input_indexed_batches());
1642 stream.gather(0).inspect(move |batch| {
1643 if Runtime::worker_index() == 0 {
1644 assert_eq!(batch, &expected_batches.next().unwrap())
1645 }
1646 });
1647
1648 Ok(handle)
1649 }
1650
1651 #[test]
1652 fn indexed_zset_test_st() {
1653 let (circuit, input_handle) =
1654 RootCircuit::build(move |circuit| indexed_zset_test_circuit(circuit)).unwrap();
1655
1656 for mut vec in input_indexed_vecs().into_iter() {
1657 input_handle.append(&mut vec);
1658 circuit.transaction().unwrap();
1659 }
1660
1661 for vec in input_indexed_vecs().into_iter() {
1662 for Tup2(k, v) in vec.into_iter() {
1663 input_handle.push(k, (v.0, v.1));
1664 }
1665 input_handle.push(5, (7, 1));
1666 input_handle.push(5, (7, -1));
1667 circuit.transaction().unwrap();
1668 }
1669 }
1670
1671 fn indexed_zset_test_mt(workers: usize) {
1672 let (mut dbsp, input_handle) =
1673 Runtime::init_circuit(workers, |circuit| indexed_zset_test_circuit(circuit)).unwrap();
1674
1675 for mut vec in input_indexed_vecs().into_iter() {
1676 input_handle.append(&mut vec);
1677 dbsp.transaction().unwrap();
1678 }
1679
1680 for vec in input_indexed_vecs().into_iter() {
1681 for Tup2(k, v) in vec.into_iter() {
1682 input_handle.push(k, (v.0, v.1));
1683 }
1684 dbsp.transaction().unwrap();
1685 }
1686
1687 dbsp.kill().unwrap();
1688 }
1689
1690 #[test]
1691 fn indexed_zset_test_mt1() {
1692 indexed_zset_test_mt(1);
1693 }
1694
1695 #[test]
1696 fn indexed_zset_test_mt4() {
1697 indexed_zset_test_mt(4);
1698 }
1699
1700 fn input_set_updates() -> Vec<Vec<Tup2<u64, bool>>> {
1701 vec![
1702 vec![Tup2(1, true), Tup2(2, true), Tup2(3, false)],
1703 vec![Tup2(1, false), Tup2(2, true), Tup2(3, true), Tup2(4, true)],
1704 vec![Tup2(2, false), Tup2(2, true), Tup2(3, true), Tup2(4, false)],
1705 vec![Tup2(2, true), Tup2(2, false)],
1706 vec![Tup2(100, true)],
1707 vec![Tup2(95, true)],
1708 vec![Tup2(80, true)],
1710 ]
1711 }
1712
1713 fn output_set_updates() -> Vec<OrdZSet<u64>> {
1714 vec![
1715 zset! { 1u64 => 1, 2 => 1},
1716 zset! { 1 => -1, 3 => 1, 4 => 1 },
1717 zset! { 4 => -1 },
1718 zset! { 2 => -1 },
1719 zset! { 100 => 1 },
1720 zset! { 95 => 1 },
1721 zset! {},
1722 ]
1723 }
1724
1725 fn set_test_circuit(circuit: &RootCircuit) -> AnyResult<SetHandle<u64>> {
1726 let (stream, handle) = circuit.add_input_set::<u64>();
1727 let watermark: Stream<_, TypedBox<u64, DynData>> =
1728 stream.waterline(|| 0, |k, ()| *k, |k1, k2| max(*k1, *k2));
1729 stream.integrate_trace_retain_keys(&watermark, |k, ts: &u64| *k >= ts.saturating_sub(10));
1730
1731 let mut expected_batches = output_set_updates().into_iter();
1732
1733 stream.gather(0).inspect(move |batch| {
1734 if Runtime::worker_index() == 0 {
1735 assert_eq!(batch, &expected_batches.next().unwrap())
1736 }
1737 });
1738
1739 Ok(handle)
1740 }
1741
1742 #[test]
1743 fn set_test_st() {
1744 let (mut circuit, mut input_handle) =
1745 Runtime::init_circuit(1, move |circuit| set_test_circuit(circuit)).unwrap();
1746
1747 for mut vec in input_set_updates().into_iter() {
1748 input_handle.append(&mut vec);
1749 circuit.transaction().unwrap();
1750 }
1751
1752 let (mut circuit, input_handle) =
1753 Runtime::init_circuit(1, move |circuit| set_test_circuit(circuit)).unwrap();
1754
1755 for vec in input_set_updates().into_iter() {
1756 for Tup2(k, b) in vec.into_iter() {
1757 input_handle.push(k, b);
1758 }
1759 circuit.transaction().unwrap();
1760 }
1761 }
1762
1763 fn set_test_mt(workers: usize) {
1764 let (mut dbsp, mut input_handle) =
1765 Runtime::init_circuit(workers, |circuit| set_test_circuit(circuit)).unwrap();
1766
1767 for mut vec in input_set_updates().into_iter() {
1768 input_handle.append(&mut vec);
1769 dbsp.transaction().unwrap();
1770 }
1771
1772 dbsp.kill().unwrap();
1773
1774 let (mut dbsp, input_handle) =
1775 Runtime::init_circuit(workers, |circuit| set_test_circuit(circuit)).unwrap();
1776
1777 for vec in input_set_updates().into_iter() {
1778 for Tup2(k, b) in vec.into_iter() {
1779 input_handle.push(k, b);
1780 }
1781 dbsp.transaction().unwrap();
1782 }
1783
1784 dbsp.kill().unwrap();
1785 }
1786
1787 #[test]
1788 fn set_test_mt1() {
1789 set_test_mt(1);
1790 }
1791
1792 #[test]
1793 fn set_test_mt4() {
1794 set_test_mt(4);
1795 }
1796
1797 fn input_map_updates1() -> Vec<Vec<Tup2<u64, Update<u64, i64>>>> {
1798 vec![
1799 vec![
1800 Tup2(1, Update::Insert(1)),
1801 Tup2(1, Update::Insert(2)),
1802 Tup2(2, Update::Delete),
1803 Tup2(3, Update::Insert(3)),
1804 ],
1805 vec![
1806 Tup2(1, Update::Insert(1)),
1807 Tup2(1, Update::Delete),
1808 Tup2(2, Update::Insert(2)),
1809 Tup2(3, Update::Insert(4)),
1810 Tup2(4, Update::Insert(4)),
1811 Tup2(4, Update::Delete),
1812 Tup2(4, Update::Insert(5)),
1813 ],
1814 vec![
1815 Tup2(1, Update::Insert(5)),
1816 Tup2(1, Update::Insert(6)),
1817 Tup2(3, Update::Delete),
1818 Tup2(4, Update::Insert(6)),
1819 ],
1820 vec![Tup2(1, Update::Insert(100))],
1822 vec![Tup2(1, Update::Insert(80))],
1824 vec![Tup2(1, Update::Insert(91))],
1825 vec![Tup2(5, Update::Insert(200))],
1827 vec![Tup2(5, Update::Insert(91))],
1829 vec![Tup2(5, Update::Insert(191))],
1830 ]
1831 }
1832
1833 fn output_map_updates1() -> Vec<OrdIndexedZSet<u64, u64>> {
1834 vec![
1835 indexed_zset! { 1u64 => {2u64 => 1}, 3 => {3 => 1}},
1836 indexed_zset! { 1 => {2 => -1}, 2 => {2 => 1}, 3 => {3 => -1, 4 => 1}, 4 => {5 => 1}},
1837 indexed_zset! { 1 => {6 => 1}, 3 => {4 => -1}, 4 => {5 => -1, 6 => 1}},
1838 indexed_zset! { 1 => {6 => -1, 100 => 1}},
1839 indexed_zset! { 1 => { 100 => -1, 80 => 1 }},
1840 indexed_zset! { 1 => {91 => 1, 80 => -1}},
1841 indexed_zset! { 5 => {200 => 1}},
1842 indexed_zset! { 5 => { 200 => -1, 91 => 1 }},
1843 indexed_zset! { 5 => {191 => 1, 91 => -1}},
1844 ]
1845 }
1846 fn input_map_updates2() -> Vec<Vec<Tup2<u64, Update<u64, i64>>>> {
1847 vec![
1848 vec![
1849 Tup2(1, Update::Insert(1)),
1851 Tup2(1, Update::Update(1)),
1852 Tup2(2, Update::Insert(1)),
1854 Tup2(2, Update::Insert(1)),
1855 Tup2(3, Update::Insert(1)),
1857 Tup2(3, Update::Delete),
1858 Tup2(4, Update::Delete),
1860 ],
1861 vec![
1862 Tup2(1, Update::Update(1)),
1864 Tup2(1, Update::Update(1)),
1865 Tup2(2, Update::Delete),
1867 Tup2(2, Update::Update(1)),
1868 Tup2(3, Update::Update(1)),
1870 Tup2(3, Update::Insert(5)),
1871 ],
1872 vec![
1873 Tup2(1, Update::Update(2)),
1875 Tup2(1, Update::Update(3)),
1876 Tup2(1, Update::Delete),
1877 Tup2(2, Update::Insert(3)),
1879 Tup2(2, Update::Update(4)),
1880 Tup2(2, Update::Delete),
1881 Tup2(3, Update::Insert(5)),
1883 ],
1884 vec![Tup2(1, Update::Insert(1)), Tup2(2, Update::Insert(5))],
1885 vec![Tup2(3, Update::Update(10))],
1887 vec![
1888 Tup2(1, Update::Update(10)),
1890 Tup2(2, Update::Update(10)),
1892 ],
1893 vec![
1894 Tup2(1, Update::Delete),
1896 Tup2(2, Update::Insert(4)),
1898 Tup2(4, Update::Insert(1)),
1900 ],
1901 vec![
1902 Tup2(2, Update::Insert(10)),
1913 Tup2(4, Update::Insert(15)),
1916 Tup2(4, Update::Insert(4)),
1917 ],
1918 vec![
1919 ],
1931 ]
1932 }
1933
1934 fn output_map_updates2() -> Vec<OrdIndexedZSet<u64, u64>> {
1935 vec![
1936 indexed_zset! { 1 => {2 => 1}, 2 => {1 => 1}},
1937 indexed_zset! { 1 => {2 => -1, 4 => 1}, 2 => {1 => -1}, 3 => { 5 => 1 } },
1938 indexed_zset! { 1 => {4 => -1} },
1939 indexed_zset! { 1 => {1 => 1}, 2 => {5=>1} },
1940 indexed_zset! { 3 => {5 => -1, 15 => 1} },
1941 indexed_zset! { 1 => {1 => -1, 11 => 1 } , 2 => {5 => -1, 15 => 1} },
1942 indexed_zset! { 1 => {11 => -1}, 2 => { 15 => -1, 4 => 1}, 4 => { 1 => 1}},
1943 indexed_zset! {2 => {4 => -1, 10 => 1}, 4 => {1 => -1, 4 => 1}},
1944 indexed_zset! {},
1945 ]
1946 }
1947
1948 fn map_test_circuit(
1949 circuit: &RootCircuit,
1950 expected_outputs: fn() -> Vec<OrdIndexedZSet<u64, u64>>,
1951 ) -> AnyResult<MapHandle<u64, u64, i64>> {
1952 let (stream, handle) =
1953 circuit.add_input_map::<u64, u64, i64, _>(|v, u| *v = ((*v as i64) + u) as u64);
1954
1955 let mut expected_batches = expected_outputs().into_iter();
1956
1957 stream.gather(0).inspect(move |batch| {
1958 if Runtime::worker_index() == 0 {
1959 assert_eq!(batch, &expected_batches.next().unwrap())
1960 }
1961 });
1962
1963 Ok(handle)
1964 }
1965
1966 #[test]
1971 fn map_test_st() {
1972 let (mut circuit, mut input_handle) = Runtime::init_circuit(1, move |circuit| {
1973 map_test_circuit(circuit, output_map_updates1)
1974 })
1975 .unwrap();
1976
1977 for mut vec in input_map_updates1().into_iter() {
1978 input_handle.append(&mut vec);
1979 circuit.transaction().unwrap();
1980 }
1981
1982 let (mut circuit, input_handle) = Runtime::init_circuit(1, move |circuit| {
1983 map_test_circuit(circuit, output_map_updates1)
1984 })
1985 .unwrap();
1986
1987 for vec in input_map_updates1().into_iter() {
1988 for Tup2(k, v) in vec.into_iter() {
1989 input_handle.push(k, v);
1990 }
1991 circuit.transaction().unwrap();
1992 }
1993 }
1994
1995 fn map_test_mt(
1996 workers: usize,
1997 inputs: fn() -> Vec<Vec<Tup2<u64, Update<u64, i64>>>>,
1998 expected_outputs: fn() -> Vec<OrdIndexedZSet<u64, u64>>,
1999 ) {
2000 let expected_outputs_clone = expected_outputs;
2001
2002 let (mut dbsp, mut input_handle) = Runtime::init_circuit(workers, move |circuit| {
2003 map_test_circuit(circuit, expected_outputs_clone)
2004 })
2005 .unwrap();
2006
2007 for mut vec in inputs().into_iter() {
2008 input_handle.append(&mut vec);
2009 dbsp.transaction().unwrap();
2010 }
2011
2012 dbsp.kill().unwrap();
2013
2014 let (mut dbsp, input_handle) = Runtime::init_circuit(workers, move |circuit| {
2015 map_test_circuit(circuit, expected_outputs)
2016 })
2017 .unwrap();
2018
2019 for vec in inputs().into_iter() {
2020 for Tup2(k, v) in vec.into_iter() {
2021 input_handle.push(k, v);
2022 }
2023 dbsp.transaction().unwrap();
2024 }
2025
2026 dbsp.kill().unwrap();
2027 }
2028
2029 #[test]
2034 fn map_test_mt1() {
2035 map_test_mt(1, input_map_updates1, output_map_updates1);
2036 map_test_mt(1, input_map_updates2, output_map_updates2);
2037 }
2038
2039 #[test]
2040 fn map_test_mt4() {
2041 map_test_mt(4, input_map_updates1, output_map_updates1);
2042 map_test_mt(4, input_map_updates2, output_map_updates2);
2043 }
2044
2045 #[test]
2050 fn map_reinsert_within_step_accumulate_output() {
2051 let (mut dbsp, (input_handle, output_handle)) = Runtime::init_circuit(1, |circuit| {
2052 let (stream, handle) =
2053 circuit.add_input_map::<u64, u64, i64, _>(|v, u| *v = ((*v as i64) + u) as u64);
2054 Ok((handle, stream.accumulate_output()))
2055 })
2056 .unwrap();
2057
2058 let initial_batch = vec![
2060 Tup2(1, Update::Insert(10)),
2061 Tup2(2, Update::Insert(20)),
2062 Tup2(3, Update::Insert(30)),
2063 ];
2064 input_handle
2066 .stage(vec![VecDeque::from(initial_batch)])
2067 .flush();
2068 dbsp.transaction().unwrap();
2069 assert_eq!(
2070 output_handle.concat().consolidate(),
2071 indexed_zset! { 1 => {10 => 1}, 2 => {20 => 1}, 3 => {30 => 1} }
2072 );
2073
2074 let delete_batch_1 = vec![
2078 Tup2(1, Update::Delete),
2079 Tup2(2, Update::Delete),
2080 Tup2(3, Update::Delete),
2081 ];
2082 input_handle
2083 .stage(vec![VecDeque::from(delete_batch_1)])
2084 .flush();
2085 let reinsert_batch_1 = vec![
2086 Tup2(1, Update::Insert(10)),
2087 Tup2(2, Update::Insert(20)),
2088 Tup2(3, Update::Insert(30)),
2089 Tup2(4, Update::Insert(40)),
2090 ];
2091 input_handle
2092 .stage(vec![VecDeque::from(reinsert_batch_1)])
2093 .flush();
2094 dbsp.transaction().unwrap();
2095 assert_eq!(
2096 output_handle.concat().consolidate(),
2097 indexed_zset! { 4 => {40 => 1} }
2098 );
2099
2100 let delete_batch_2 = vec![
2102 Tup2(1, Update::Delete),
2103 Tup2(2, Update::Delete),
2104 Tup2(3, Update::Delete),
2105 Tup2(4, Update::Delete),
2106 ];
2107 input_handle
2108 .stage(vec![VecDeque::from(delete_batch_2)])
2109 .flush();
2110 let reinsert_batch_2 = vec![
2111 Tup2(1, Update::Insert(10)),
2112 Tup2(2, Update::Insert(20)),
2113 Tup2(3, Update::Insert(30)),
2114 Tup2(4, Update::Insert(40)),
2115 Tup2(5, Update::Insert(50)),
2116 ];
2117 input_handle
2118 .stage(vec![VecDeque::from(reinsert_batch_2)])
2119 .flush();
2120 dbsp.transaction().unwrap();
2121 assert_eq!(
2122 output_handle.concat().consolidate(),
2123 indexed_zset! {
2124 5 => {50 => 1}
2125 }
2126 );
2127
2128 dbsp.kill().unwrap();
2129 }
2130
2131 fn partition_into_k_contiguous_batches<T: Clone>(
2133 items: Vec<T>,
2134 k: usize,
2135 rng: &mut ChaCha8Rng,
2136 ) -> Vec<Vec<T>> {
2137 let n = items.len();
2138 debug_assert!(k >= 1);
2139 if n == 0 {
2140 return vec![];
2141 }
2142 let k = k.min(n).max(1);
2143 if k == 1 {
2144 return vec![items];
2145 }
2146 let split_at: Vec<usize> = index::sample(rng, n - 1, k - 1)
2147 .into_iter()
2148 .map(|i| i + 1)
2149 .collect();
2150 let mut split_at = split_at;
2151 split_at.sort_unstable();
2152 let mut out = Vec::with_capacity(k);
2153 let mut start = 0usize;
2154 for cut in split_at {
2155 out.push(items[start..cut].to_vec());
2156 start = cut;
2157 }
2158 out.push(items[start..].to_vec());
2159 out
2160 }
2161
2162 fn apply_map_update(state: &mut HashMap<u64, u64>, key: u64, upd: Update<u64, u64>) {
2163 match upd {
2164 Update::Insert(v) => {
2165 state.insert(key, v);
2166 }
2167 Update::Delete => {
2168 state.remove(&key);
2169 }
2170 Update::Update(v) => {
2171 if state.contains_key(&key) {
2172 state.insert(key, v);
2173 }
2174 }
2175 }
2176 }
2177
2178 fn indexed_zset_state_diff(
2179 before: &HashMap<u64, u64>,
2180 after: &HashMap<u64, u64>,
2181 ) -> OrdIndexedZSet<u64, u64> {
2182 let keys: BTreeSet<u64> = before.keys().chain(after.keys()).copied().collect();
2183 let mut tuples = Vec::new();
2184 for k in keys {
2185 let old_v = before.get(&k).copied();
2186 let new_v = after.get(&k).copied();
2187 match (old_v, new_v) {
2188 (None, None) => {}
2189 (None, Some(nv)) => tuples.push(Tup2(Tup2(k, nv), 1)),
2190 (Some(ov), None) => tuples.push(Tup2(Tup2(k, ov), -1)),
2191 (Some(ov), Some(nv)) if ov != nv => {
2192 tuples.push(Tup2(Tup2(k, ov), -1));
2193 tuples.push(Tup2(Tup2(k, nv), 1));
2194 }
2195 _ => {}
2196 }
2197 }
2198 OrdIndexedZSet::from_tuples((), tuples)
2199 }
2200
2201 #[test]
2207 fn randomized_input_map_test() {
2208 let (mut dbsp, (input_handle, output_handle)) = Runtime::init_circuit(1, |circuit| {
2209 let (stream, handle) = circuit.add_input_map::<u64, u64, u64, _>(|v, u| *v = *u);
2210 Ok((handle, stream.accumulate_output()))
2211 })
2212 .unwrap();
2213
2214 let mut state: HashMap<u64, u64> = HashMap::new();
2215 let mut rng = ChaCha8Rng::seed_from_u64(0x_6D61_705F_7374_6167_u64);
2216
2217 for _step in 0..50000 {
2218 let before = state.clone();
2220 let num_updates = rng.gen_range(0..=12);
2221
2222 let updates: Vec<Tup2<u64, Update<u64, u64>>> = (0..num_updates)
2223 .map(|_| {
2224 let key = rng.gen_range(1u64..=3);
2225 let upd = match rng.gen_range(0..3) {
2226 0 => Update::Insert(rng.gen_range(0u64..512)),
2227 1 => Update::Delete,
2228 2 => Update::Update(rng.gen_range(0u64..=512)),
2229 _ => unreachable!(),
2230 };
2231 Tup2(key, upd)
2232 })
2233 .collect();
2234
2235 for Tup2(k, u) in &updates {
2236 apply_map_update(&mut state, *k, u.clone());
2237 }
2238
2239 let num_batches = if num_updates == 0 {
2240 1usize
2241 } else {
2242 rng.gen_range(1..=std::cmp::min(3, num_updates))
2243 };
2244
2245 let batches = if num_updates == 0 {
2246 vec![Vec::new()]
2247 } else {
2248 partition_into_k_contiguous_batches(updates, num_batches, &mut rng)
2249 };
2250
2251 for batch in batches {
2252 input_handle.stage(once(VecDeque::from(batch))).flush();
2254 }
2255
2256 dbsp.transaction().unwrap();
2257
2258 let expected = indexed_zset_state_diff(&before, &state);
2259 assert_eq!(
2260 output_handle.concat().consolidate(),
2261 expected,
2262 "accumulated output should equal the net map change for this transaction"
2263 );
2264 }
2265
2266 dbsp.kill().unwrap();
2267 }
2268
2269 fn map_with_waterline_test_circuit(
2270 circuit: &RootCircuit,
2271 ) -> (
2272 MapHandle<u64, u64, i64>,
2273 OutputHandle<TypedBox<u64, DynData>>,
2274 OutputHandle<OrdIndexedZSet<u64, u64>>,
2275 OutputHandle<OrdZSet<String>>,
2276 ) {
2277 let (stream, errors, waterline, input_handle) = circuit
2278 .add_input_map_with_waterline::<u64, u64, i64, u64, String, _, _, _, _, _, _>(
2279 |v, u| *v = ((*v as i64) + u) as u64,
2280 || 0u64,
2281 |_k, v| *v,
2282 |wl1, wl2| max(*wl1, *wl2),
2283 |wl, _k, v| *v >= *wl,
2284 |wl, k, v, w| format!("waterline: {wl}, key: {k}, value: {v}, weight: {w}"),
2285 );
2286
2287 let output_handle = stream.output();
2288 let waterline_output_handle = waterline.output();
2289 let errors_handle = errors.output();
2290
2291 (
2292 input_handle,
2293 waterline_output_handle,
2294 output_handle,
2295 errors_handle,
2296 )
2297 }
2298
2299 fn map_with_waterline_test(
2301 workers: usize,
2302 inputs: fn() -> Vec<Vec<Tup2<u64, Update<u64, i64>>>>,
2303 expected_outputs: fn() -> Vec<(OrdIndexedZSet<u64, u64>, OrdZSet<String>, u64)>,
2304 ) {
2305 let expected_outputs = expected_outputs();
2306
2307 let (mut dbsp, (mut input_handle, waterline_handle, output_handle, errors_handle)) =
2308 Runtime::init_circuit(workers, move |circuit| {
2309 Ok(map_with_waterline_test_circuit(circuit))
2310 })
2311 .unwrap();
2312
2313 for (step, mut vec) in inputs().into_iter().enumerate() {
2314 input_handle.append(&mut vec);
2315 dbsp.transaction().unwrap();
2316 let output = output_handle.consolidate();
2317 assert_eq!(
2318 *waterline_handle.take_from_worker(0).unwrap(),
2319 expected_outputs[step].2
2320 );
2321 assert_eq!(output, expected_outputs[step].0);
2322
2323 let errors = errors_handle.consolidate();
2324 assert_eq!(errors, expected_outputs[step].1);
2325 }
2326
2327 dbsp.kill().unwrap();
2328 }
2329
2330 fn input_map_with_waterline_updates1() -> Vec<Vec<Tup2<u64, Update<u64, i64>>>> {
2331 vec![
2332 vec![
2333 Tup2(1, Update::Insert(1)),
2334 Tup2(1, Update::Insert(1)),
2335 Tup2(2, Update::Delete), Tup2(3, Update::Insert(1)),
2337 ], vec![
2339 Tup2(1, Update::Insert(1)), Tup2(1, Update::Delete), Tup2(2, Update::Insert(2)), Tup2(3, Update::Insert(3)), Tup2(4, Update::Insert(3)), Tup2(4, Update::Delete), Tup2(4, Update::Insert(5)), ], vec![
2348 Tup2(1, Update::Insert(5)), Tup2(2, Update::Insert(6)), Tup2(3, Update::Delete), Tup2(4, Update::Insert(6)), ], vec![
2354 Tup2(5, Update::Insert(5)), Tup2(6, Update::Insert(6)), Tup2(7, Update::Insert(7)), Tup2(4, Update::Insert(8)), ], ]
2360 }
2361
2362 fn output_map_with_waterline_updates1() -> Vec<(OrdIndexedZSet<u64, u64>, OrdZSet<String>, u64)>
2363 {
2364 vec![
2365 (
2366 indexed_zset! { 1u64 => {1u64 => 1}, 3 => {1 => 1} },
2367 zset! {},
2368 1,
2369 ),
2370 (
2371 indexed_zset! { 1 => {1 => -1}, 2 => {2 => 1}, 3 => {1 => -1, 3 => 1}, 4 => {5 => 1} },
2372 zset! {},
2373 5,
2374 ),
2375 (
2376 indexed_zset! { 1 => {5 => 1}, 4 => {5 => -1, 6 => 1} },
2377 zset! { "waterline: 5, key: 2, value: 2, weight: -1".to_string() => 1, "waterline: 5, key: 3, value: 3, weight: -1".to_string() => 1 },
2378 6,
2379 ),
2380 (
2381 indexed_zset! { 4 => {6 => -1, 8 => 1}, 6 => {6 => 1}, 7 => {7 => 1} },
2382 zset! {"waterline: 6, key: 5, value: 5, weight: 1".to_string() => 1},
2383 8,
2384 ),
2385 ]
2386 }
2387
2388 #[test]
2389 fn map_with_waterline_test_mt() {
2390 map_with_waterline_test(
2391 4,
2392 input_map_with_waterline_updates1,
2393 output_map_with_waterline_updates1,
2394 );
2395 }
2396
2397 fn map_with_waterline_gc_test_circuit(
2398 circuit: &RootCircuit,
2399 ) -> (
2400 MapHandle<u64, u64, i64>,
2401 OutputHandle<TypedBox<u64, DynData>>,
2402 OutputHandle<OrdIndexedZSet<u64, u64>>,
2403 OutputHandle<OrdZSet<String>>,
2404 ) {
2405 let (stream, errors, waterline, input_handle) = circuit
2406 .add_input_map_with_waterline::<u64, u64, i64, u64, String, _, _, _, _, _, _>(
2407 |v, u| *v = ((*v as i64) + u) as u64,
2408 || 0u64,
2409 |k, _v| *k,
2410 |wl1, wl2| max(*wl1, *wl2),
2411 |wl, k, _v| *k >= *wl,
2412 |wl, k, v, w| format!("waterline: {wl}, key: {k}, value: {v}, weight: {w}"),
2413 );
2414
2415 stream.integrate_trace_retain_keys(&waterline, |key, wl| *key >= *wl);
2416
2417 let output_handle = stream.output();
2418 let waterline_output_handle = waterline.output();
2419 let errors_handle = errors.output();
2420
2421 (
2422 input_handle,
2423 waterline_output_handle,
2424 output_handle,
2425 errors_handle,
2426 )
2427 }
2428
2429 fn map_with_waterline_gc_test(
2432 workers: usize,
2433 inputs: fn() -> Vec<Vec<Tup2<u64, Update<u64, i64>>>>,
2434 expected_outputs: fn() -> Vec<(OrdIndexedZSet<u64, u64>, OrdZSet<String>, u64)>,
2435 ) {
2436 let expected_outputs = expected_outputs();
2437
2438 let (mut dbsp, (mut input_handle, waterline_handle, output_handle, errors_handle)) =
2439 Runtime::init_circuit(workers, move |circuit| {
2440 Ok(map_with_waterline_gc_test_circuit(circuit))
2441 })
2442 .unwrap();
2443
2444 for (step, mut vec) in inputs().into_iter().enumerate() {
2445 input_handle.append(&mut vec);
2446 dbsp.transaction().unwrap();
2447 let output = output_handle.consolidate();
2448 assert_eq!(
2449 *waterline_handle.take_from_worker(0).unwrap(),
2450 expected_outputs[step].2
2451 );
2452 assert_eq!(output, expected_outputs[step].0);
2453
2454 let errors = errors_handle.consolidate();
2455 assert_eq!(errors, expected_outputs[step].1);
2456 }
2457
2458 dbsp.kill().unwrap();
2459 }
2460
2461 fn input_map_with_waterline_gc_updates1() -> Vec<Vec<Tup2<u64, Update<u64, i64>>>> {
2462 vec![
2463 vec![
2464 Tup2(1, Update::Insert(1)),
2465 Tup2(1, Update::Insert(1)),
2466 Tup2(2, Update::Delete), Tup2(3, Update::Insert(1)),
2468 ], vec![
2470 Tup2(1, Update::Insert(1)), Tup2(1, Update::Delete), Tup2(2, Update::Insert(2)), Tup2(3, Update::Insert(3)), Tup2(3, Update::Insert(4)), Tup2(4, Update::Insert(3)), Tup2(4, Update::Delete), Tup2(4, Update::Insert(5)), ], vec![
2480 Tup2(3, Update::Delete), Tup2(5, Update::Insert(6)), Tup2(5, Update::Delete), ], vec![
2485 Tup2(5, Update::Insert(5)), Tup2(6, Update::Insert(6)), Tup2(7, Update::Insert(7)), ], ]
2490 }
2491
2492 fn output_map_with_waterline_gc_updates1()
2493 -> Vec<(OrdIndexedZSet<u64, u64>, OrdZSet<String>, u64)> {
2494 vec![
2495 (
2496 indexed_zset! { 1u64 => {1u64 => 1}, 3 => {1 => 1} },
2497 zset! {},
2498 3,
2499 ),
2500 (
2501 indexed_zset! { 3 => {1 => -1, 4 => 1}, 4 => {5 => 1} },
2502 zset! {"waterline: 3, key: 1, value: 1, weight: -1".to_string() => 1, "waterline: 3, key: 2, value: 2, weight: 1".to_string() => 1},
2503 4,
2504 ),
2505 (
2506 indexed_zset! {},
2507 zset! {"waterline: 4, key: 3, value: 4, weight: -1".to_string() => 1},
2508 4,
2509 ),
2510 (
2511 indexed_zset! { 5 => {5 => 1}, 6 => {6 => 1}, 7 => {7 => 1} },
2512 zset! {},
2513 7,
2514 ),
2515 ]
2516 }
2517
2518 #[test]
2519 fn map_with_waterline_gc_test_mt() {
2520 map_with_waterline_gc_test(
2521 4,
2522 input_map_with_waterline_gc_updates1,
2523 output_map_with_waterline_gc_updates1,
2524 );
2525 }
2526}