solana_streamer_sdk/streaming/common/
order_buffer.rs1use crate::streaming::event_parser::DexEvent;
2use std::collections::{BTreeMap, HashMap};
3use tokio::time::Instant;
4
5#[derive(Default)]
6pub struct SlotBuffer {
7 slots: BTreeMap<u64, Vec<(u64, DexEvent)>>,
8 current_slot: u64,
9 last_flush_time: Option<Instant>,
10 streaming_watermarks: HashMap<u64, u64>,
11}
12
13impl SlotBuffer {
14 #[inline]
15 pub fn new() -> Self {
16 Self {
17 slots: BTreeMap::new(),
18 current_slot: 0,
19 last_flush_time: Some(Instant::now()),
20 streaming_watermarks: HashMap::new(),
21 }
22 }
23
24 #[inline]
25 pub fn push(&mut self, slot: u64, tx_index: u64, event: DexEvent) {
26 if self.slots.is_empty() {
27 self.last_flush_time = Some(Instant::now());
28 }
29 self.slots.entry(slot).or_default().push((tx_index, event));
30 if slot > self.current_slot {
31 self.current_slot = slot;
32 }
33 }
34
35 #[inline]
36 pub fn is_empty(&self) -> bool {
37 self.slots.is_empty()
38 }
39
40 pub fn flush_before(&mut self, current_slot: u64) -> Vec<DexEvent> {
41 let keep_slots = self.slots.split_off(¤t_slot);
42 let flush_slots = std::mem::replace(&mut self.slots, keep_slots);
43
44 let mut result = Vec::with_capacity(flush_slots.values().map(Vec::len).sum());
45 for (_slot, mut events) in flush_slots {
46 events.sort_by_key(|(idx, _)| *idx);
48 result.extend(events.into_iter().map(|(_, event)| event));
49 }
50
51 if !result.is_empty() {
52 self.last_flush_time = Some(Instant::now());
53 }
54 result
55 }
56
57 pub fn flush_all(&mut self) -> Vec<DexEvent> {
58 let all_slots = std::mem::take(&mut self.slots);
59 let mut result = Vec::with_capacity(all_slots.values().map(Vec::len).sum());
60
61 for (_slot, mut events) in all_slots {
62 events.sort_by_key(|(idx, _)| *idx);
64 result.extend(events.into_iter().map(|(_, event)| event));
65 }
66
67 if !result.is_empty() {
68 self.last_flush_time = Some(Instant::now());
69 }
70 result
71 }
72
73 #[inline]
74 pub fn should_timeout(&self, timeout_ms: u64) -> bool {
75 self.last_flush_time
76 .map(|t| !self.slots.is_empty() && t.elapsed().as_millis() as u64 > timeout_ms)
77 .unwrap_or(false)
78 }
79
80 pub fn push_streaming(
88 &mut self,
89 slot: u64,
90 tx_index: u64,
91 events: Vec<DexEvent>,
92 ) -> Vec<DexEvent> {
93 let mut result = Vec::new();
94 if events.is_empty() {
95 return result;
96 }
97
98 if slot > self.current_slot && self.current_slot > 0 {
99 let keep_slots = self.slots.split_off(&slot);
100 let flush_slots = std::mem::replace(&mut self.slots, keep_slots);
101 result.reserve(flush_slots.values().map(Vec::len).sum());
102 for (old_slot, mut buffered) in flush_slots {
103 buffered.sort_by_key(|(idx, _)| *idx);
105 result.extend(buffered.into_iter().map(|(_, event)| event));
106 self.streaming_watermarks.remove(&old_slot);
107 }
108 }
109
110 if slot > self.current_slot {
111 self.current_slot = slot;
112 }
113
114 let next_expected = *self.streaming_watermarks.get(&slot).unwrap_or(&0);
115
116 if tx_index == next_expected {
117 result.reserve(events.len());
118 result.extend(events);
119 let mut watermark = next_expected + 1;
120
121 let remove_empty_slot = if let Some(buffered) = self.slots.get_mut(&slot) {
122 buffered.sort_by_key(|(idx, _)| *idx);
124 let mut ready_count = 0;
128 while ready_count < buffered.len() && buffered[ready_count].0 == watermark {
129 let idx = watermark;
130 while ready_count < buffered.len() && buffered[ready_count].0 == idx {
131 ready_count += 1;
132 }
133 watermark = idx + 1;
134 }
135 result.reserve(ready_count);
136 for (_, event) in buffered.drain(..ready_count) {
137 result.push(event);
138 }
139 buffered.is_empty()
140 } else {
141 false
142 };
143 if remove_empty_slot {
144 self.slots.remove(&slot);
145 }
146 self.streaming_watermarks.insert(slot, watermark);
147 } else if tx_index > next_expected {
148 if self.slots.is_empty() {
149 self.last_flush_time = Some(Instant::now());
150 }
151 let buffered = self.slots.entry(slot).or_default();
152 buffered.reserve(events.len());
153 for event in events {
154 buffered.push((tx_index, event));
155 }
156 }
157
158 if !result.is_empty() {
159 self.last_flush_time = Some(Instant::now());
160 }
161 result
162 }
163
164 pub fn flush_streaming_timeout(&mut self) -> Vec<DexEvent> {
165 let flush_slots = std::mem::take(&mut self.slots);
166 let mut result = Vec::with_capacity(flush_slots.values().map(Vec::len).sum());
167 for (slot, mut events) in flush_slots {
168 events.sort_by_key(|(idx, _)| *idx);
170 result.extend(events.into_iter().map(|(_, event)| event));
171 self.streaming_watermarks.remove(&slot);
172 }
173 if !result.is_empty() {
174 self.last_flush_time = Some(Instant::now());
175 }
176 result
177 }
178}
179
180pub struct MicroBatchBuffer {
181 events: Vec<(u64, u64, DexEvent)>,
182 window_start_us: i64,
183}
184
185impl MicroBatchBuffer {
186 #[inline]
187 pub fn new() -> Self {
188 Self { events: Vec::with_capacity(64), window_start_us: 0 }
189 }
190
191 #[inline]
192 pub fn push(
193 &mut self,
194 slot: u64,
195 tx_index: u64,
196 event: DexEvent,
197 now_us: i64,
198 window_us: u64,
199 ) -> bool {
200 if self.events.is_empty() {
201 self.window_start_us = now_us;
202 }
203 self.events.push((slot, tx_index, event));
204 (now_us - self.window_start_us) as u64 >= window_us
205 }
206
207 #[inline]
208 pub fn flush(&mut self) -> Vec<DexEvent> {
209 if self.events.is_empty() {
210 return Vec::new();
211 }
212
213 self.events.sort_by_key(|(slot, tx_index, _)| (*slot, *tx_index));
215 let mut result = Vec::with_capacity(self.events.len());
216 result.extend(self.events.drain(..).map(|(_, _, event)| event));
217 self.window_start_us = 0;
218 result
219 }
220
221 #[inline]
222 pub fn should_flush(&self, now_us: i64, window_us: u64) -> bool {
223 !self.events.is_empty() && (now_us - self.window_start_us) as u64 >= window_us
224 }
225
226 #[inline]
227 pub fn is_empty(&self) -> bool {
228 self.events.is_empty()
229 }
230}
231
232impl Default for MicroBatchBuffer {
233 fn default() -> Self {
234 Self::new()
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use crate::streaming::event_parser::protocols::BlockMetaEvent;
242
243 fn event(id: u64) -> DexEvent {
244 DexEvent::BlockMetaEvent(BlockMetaEvent::new(id, id.to_string(), 0, 0))
245 }
246
247 fn ids(events: Vec<DexEvent>) -> Vec<u64> {
248 events
249 .into_iter()
250 .map(|event| match event {
251 DexEvent::BlockMetaEvent(event) => event.slot,
252 _ => unreachable!("test only creates block meta events"),
253 })
254 .collect()
255 }
256
257 #[test]
258 fn flush_before_keeps_newer_slots_and_sorts_flushed_events() {
259 let mut buffer = SlotBuffer::new();
260 buffer.push(7, 2, event(72));
261 buffer.push(5, 1, event(51));
262 buffer.push(5, 0, event(50));
263
264 assert_eq!(ids(buffer.flush_before(6)), vec![50, 51]);
265 assert_eq!(ids(buffer.flush_all()), vec![72]);
266 }
267
268 #[test]
269 fn streaming_order_drains_only_contiguous_ready_prefix() {
270 let mut buffer = SlotBuffer::new();
271
272 assert!(buffer.push_streaming(10, 3, vec![event(103)]).is_empty());
273 assert!(buffer.push_streaming(10, 1, vec![event(101)]).is_empty());
274 assert_eq!(ids(buffer.push_streaming(10, 0, vec![event(100)])), vec![100, 101]);
275 assert_eq!(ids(buffer.push_streaming(10, 2, vec![event(102)])), vec![102, 103]);
276 assert!(buffer.is_empty());
277 }
278
279 #[test]
280 fn streaming_order_keeps_every_event_of_a_multi_event_transaction() {
281 let mut buffer = SlotBuffer::new();
282
283 assert_eq!(
285 ids(buffer.push_streaming(10, 0, vec![event(100), event(101), event(102)])),
286 vec![100, 101, 102]
287 );
288 assert_eq!(ids(buffer.push_streaming(10, 1, vec![event(110)])), vec![110]);
290 assert!(buffer.is_empty());
291 }
292
293 #[test]
294 fn streaming_order_releases_buffered_multi_event_transactions_in_order() {
295 let mut buffer = SlotBuffer::new();
296
297 assert!(buffer.push_streaming(10, 1, vec![event(110), event(111)]).is_empty());
299 assert_eq!(
301 ids(buffer.push_streaming(10, 0, vec![event(100), event(101)])),
302 vec![100, 101, 110, 111]
303 );
304 assert!(buffer.is_empty());
305 }
306
307 #[test]
308 fn micro_batch_flush_sorts_and_reuses_allocation() {
309 let mut buffer = MicroBatchBuffer::new();
310 let initial_capacity = buffer.events.capacity();
311
312 assert!(!buffer.push(2, 1, event(21), 0, 100));
313 assert!(!buffer.push(1, 0, event(10), 10, 100));
314
315 assert_eq!(ids(buffer.flush()), vec![10, 21]);
316 assert!(buffer.events.capacity() >= initial_capacity);
317
318 assert!(!buffer.push(3, 0, event(30), 200, 100));
319 assert_eq!(ids(buffer.flush()), vec![30]);
320 }
321}