krabka_client_streams/dsl/
sliding_windowed_cogrouped.rs1use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc, sync::Arc};
9
10use crate::{
11 dsl::{
12 builder::InternalStreamsBuilder,
13 cogrouped::{
14 CogroupInput, CogroupKind, CogroupSpec, CogroupedKStream, StoreRegistrarFn,
15 lower_cogroup,
16 },
17 config::Materialized,
18 kgrouped::mint_store_name,
19 ktable::KTable,
20 names,
21 sliding_windowed_kgrouped::sliding_suppress_factory,
22 windows::{SlidingWindows, TimeWindowedSerde, Windowed},
23 },
24 processor::serde::Serde,
25};
26
27impl<K, VOut> CogroupedKStream<K, VOut>
28where
29 K: Any + Send + Sync + Clone,
30 VOut: Any + Send + Sync + Clone,
31{
32 #[must_use]
35 pub fn windowed_by_sliding(
36 self,
37 windows: SlidingWindows,
38 ) -> SlidingWindowedCogroupedStream<K, VOut> {
39 SlidingWindowedCogroupedStream {
40 builder: self.builder,
41 inputs: self.inputs,
42 windows,
43 _pd: PhantomData,
44 }
45 }
46}
47
48pub struct SlidingWindowedCogroupedStream<K, VOut> {
51 builder: Rc<RefCell<InternalStreamsBuilder>>,
52 inputs: Vec<CogroupInput<K, VOut>>,
53 windows: SlidingWindows,
54 _pd: PhantomData<fn() -> (K, VOut)>,
55}
56
57impl<K, VOut> SlidingWindowedCogroupedStream<K, VOut>
58where
59 K: Any + Send + Sync + Clone,
60 VOut: Any + Send + Sync + Clone,
61{
62 pub fn aggregate_explicit<KS, VS, I>(
68 self,
69 init: I,
70 materialized: impl Into<Materialized<KS, VS>>,
71 ) -> KTable<Windowed<K>, VOut, TimeWindowedSerde<KS>, VS>
72 where
73 KS: Serde<K> + Clone + 'static,
74 VS: Serde<VOut> + Clone + 'static,
75 I: Fn() -> VOut + Send + Sync + 'static,
76 {
77 let materialized = materialized.into();
78 let store_name = mint_store_name(&self.builder, &materialized, names::AGGREGATE_STORE);
79 let suppress_factory = sliding_suppress_factory::<K, VOut, KS, VS>(
80 materialized.key_serde.clone(),
81 materialized.value_serde.clone(),
82 self.windows,
83 );
84 let Materialized {
85 key_serde,
86 value_serde,
87 logging,
88 caching,
89 ..
90 } = materialized;
91 let spec = CogroupSpec::<K, VOut> {
92 kind: CogroupKind::Sliding(self.windows),
93 init: Arc::new(init),
94 merger: None,
95 };
96 let ks = key_serde.clone();
97 let vs = value_serde.clone();
98 let store_for_reg = store_name.clone();
99 let size = self.windows.time_difference * 2.0;
104 let window_size = self.windows.time_difference;
107 let grace = self.windows.grace;
108 let registrar: StoreRegistrarFn = Box::new(move |state, procs| {
109 state.topology.add_window_store::<K, VOut, KS, VS>(
110 store_for_reg.clone(),
111 ks.clone(),
112 vs.clone(),
113 (size, window_size, grace),
114 procs,
115 );
116 state.topology.mark_store_caching(&store_for_reg, caching);
117 });
118 let merge_id = lower_cogroup::<K, VOut, Windowed<K>>(
119 &self.builder,
120 self.inputs,
121 &store_name,
122 &spec,
123 logging,
124 registrar,
125 );
126 KTable::new(
127 Rc::clone(&self.builder),
128 merge_id,
129 Some(store_name),
130 None,
131 TimeWindowedSerde::new(key_serde, self.windows.time_difference),
132 value_serde,
133 )
134 .with_window_grace(Some(self.windows.grace))
135 .with_suppress_factory(Some(suppress_factory))
136 }
137}
138
139#[cfg(test)]
140mod caching_tests {
141 use assert2::check;
142 use krabka_units::prelude::*;
143
144 use crate::{
145 BufferConfig, I64Serde, Materialized, Produced, SlidingWindows, StringSerde, Suppressed,
146 dsl::{StreamsBuilder, windows::TimeWindowedSerde},
147 store::backend::StoreBackend,
148 };
149
150 #[test]
151 fn sliding_windowed_cogroup_marks_store_cached() {
152 let b = StreamsBuilder::new();
153 let g1 = b.stream::<String, String>(["in1"]).group_by_key();
154 let g2 = b.stream::<String, String>(["in2"]).group_by_key();
155 g1.cogroup::<i64, _>(|_k, v: &String, acc| {
156 acc + i64::try_from(v.len()).unwrap_or(i64::MAX)
157 })
158 .cogroup(g2, |_k, _v: &String, acc| acc + 1)
159 .windowed_by_sliding(SlidingWindows::of_time_difference_with_no_grace(millis(
160 100,
161 )))
162 .aggregate_explicit(
163 || 0i64,
164 Materialized::with(StringSerde, I64Serde).as_store("cg"),
165 )
166 .suppress(Suppressed::until_window_closes(BufferConfig::unbounded()))
167 .to_stream()
168 .to_explicit(
169 "out",
170 Produced::with(TimeWindowedSerde::new(StringSerde, millis(100)), I64Serde),
171 );
172 let built = b.build("app").unwrap();
173 let g = pollster::block_on(built.instantiate(&StoreBackend::InMemory, "app", kibibytes(1)))
174 .unwrap();
175 check!(g.cache_owner.contains_key("cg"));
176 }
177
178 #[test]
179 fn sliding_windowed_cogroup_uncached_when_off() {
180 let b = StreamsBuilder::new();
181 let g1 = b.stream::<String, String>(["in1"]).group_by_key();
182 let g2 = b.stream::<String, String>(["in2"]).group_by_key();
183 g1.cogroup::<i64, _>(|_k, v: &String, acc| {
184 acc + i64::try_from(v.len()).unwrap_or(i64::MAX)
185 })
186 .cogroup(g2, |_k, _v: &String, acc| acc + 1)
187 .windowed_by_sliding(SlidingWindows::of_time_difference_with_no_grace(millis(
188 100,
189 )))
190 .aggregate_explicit(
191 || 0i64,
192 Materialized::with(StringSerde, I64Serde)
193 .as_store("cg")
194 .with_caching(false),
195 )
196 .to_stream()
197 .to_explicit(
198 "out",
199 Produced::with(TimeWindowedSerde::new(StringSerde, millis(100)), I64Serde),
200 );
201 let built = b.build("app").unwrap();
202 let g = pollster::block_on(built.instantiate(&StoreBackend::InMemory, "app", kibibytes(1)))
203 .unwrap();
204 check!(!g.cache_owner.contains_key("cg"));
205 }
206}