mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-24 14:28:10 +00:00
fix events being dropped in streaming ordered mode for multi event transactions
in StreamingOrdered mode the slot buffer is fed one event at a time, but every event parsed from the same transaction carries that transaction's tx_index. push_streaming bumps the streaming watermark to tx_index + 1 as soon as it releases the first event, so the rest of that transaction's events have tx_index < watermark, fall into the "already delivered" branch and get dropped without any trace. so any transaction that parses into more than one dex event (a swap route that touches several pools, a create + buy, and so on) only ever delivered its first event when order_mode was StreamingOrdered. Unordered, Ordered and MicroBatch were not affected. fix: - push_streaming now takes all events of one (slot, tx_index) as a group so the watermark advances once per transaction instead of once per event - the buffered drain advances the watermark per distinct tx_index, since a slot can now hold several events under the same index - the ordered buffers use a stable sort so events inside one transaction keep the order the parser produced them in, sort_unstable could shuffle equal keys added tests for the multi event transaction case and for releasing buffered out of order multi event transactions.
This commit is contained in:
@@ -464,9 +464,13 @@ fn handle_ordered_transaction(
|
||||
}
|
||||
}
|
||||
OrderMode::StreamingOrdered => {
|
||||
for event in events {
|
||||
let (slot, tx_index) = event_order_key(&event, fallback_slot, fallback_tx_index);
|
||||
deliver_events(callback.clone(), slot_buffer.push_streaming(slot, tx_index, event));
|
||||
// Events parsed from one transaction share its (slot, tx_index); push them as a
|
||||
// single group so the streaming watermark advances per transaction and no event of
|
||||
// a multi-event transaction is dropped.
|
||||
for ((slot, tx_index), group) in
|
||||
group_events_by_order_key(events, fallback_slot, fallback_tx_index)
|
||||
{
|
||||
deliver_events(callback.clone(), slot_buffer.push_streaming(slot, tx_index, group));
|
||||
}
|
||||
}
|
||||
OrderMode::MicroBatch => {
|
||||
@@ -541,6 +545,25 @@ fn event_order_key(event: &DexEvent, fallback_slot: u64, fallback_tx_index: u64)
|
||||
(metadata.slot.max(fallback_slot), metadata.tx_index.unwrap_or(fallback_tx_index))
|
||||
}
|
||||
|
||||
/// Group consecutive events that share the same `(slot, tx_index)` order key, preserving order.
|
||||
/// Events of a single transaction carry the same key, so this normally yields one group, but it
|
||||
/// stays correct if a transaction ever spans multiple keys.
|
||||
fn group_events_by_order_key(
|
||||
events: Vec<DexEvent>,
|
||||
fallback_slot: u64,
|
||||
fallback_tx_index: u64,
|
||||
) -> Vec<((u64, u64), Vec<DexEvent>)> {
|
||||
let mut groups: Vec<((u64, u64), Vec<DexEvent>)> = Vec::new();
|
||||
for event in events {
|
||||
let key = event_order_key(&event, fallback_slot, fallback_tx_index);
|
||||
match groups.last_mut() {
|
||||
Some((last_key, group)) if *last_key == key => group.push(event),
|
||||
_ => groups.push((key, vec![event])),
|
||||
}
|
||||
}
|
||||
groups
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deliver_events(callback: Arc<dyn Fn(DexEvent) + Send + Sync>, events: Vec<DexEvent>) {
|
||||
for event in events {
|
||||
|
||||
Reference in New Issue
Block a user