use std::collections::VecDeque;
use std::sync::Arc;
use crate::column::Value;
use crate::error::Result;
use crate::query::EventKey;
use crate::reducer::Agg;
use crate::series::ColumnDef;
use super::aggregation::plan_mapping;
use super::buffer::EventBuffer;
use super::event::{LiveEvent, LiveSchema};
use super::rolling::{Trigger, TriggerKind, clock_crossed};
use super::states::LiveState;
const CALL: &str = "LiveSeries::reduce";
#[derive(Debug, Clone, Copy)]
pub(crate) struct Place {
pub(crate) at: usize,
}
pub struct LiveReduce {
name: String,
schema: Arc<LiveSchema>,
columns: Vec<(usize, LiveState)>,
held: VecDeque<usize>,
next: usize,
trigger: TriggerKind,
last_bucket: Option<i128>,
since_emit: usize,
pending: Option<EventKey>,
out: EventBuffer,
events_observed: u64,
evictions: u64,
emissions: u64,
}
impl std::fmt::Debug for LiveReduce {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LiveReduce")
.field("name", &self.name)
.field("len", &self.out.len())
.finish_non_exhaustive()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReduceStats {
pub events_observed: u64,
pub evictions: u64,
pub emissions: u64,
pub buffer_size: u64,
}
impl LiveReduce {
pub(crate) fn new(
name: String,
source: &[ColumnDef],
mapping: &[Agg],
trigger: Trigger,
sorted: bool,
history: impl Iterator<Item = LiveEvent>,
) -> Result<Self> {
trigger.check_key(CALL, source[0].kind)?;
let (defs, plan) = plan_mapping(CALL, source[0].clone(), source, mapping)?;
let schema = LiveSchema::new(defs);
let mut reduce = Self {
name,
out: EventBuffer::new(Arc::clone(&schema)),
schema,
columns: plan
.into_iter()
.map(|(src, r)| (src, LiveState::new(r, sorted)))
.collect(),
held: VecDeque::new(),
next: 0,
trigger: trigger.0,
last_bucket: None,
since_emit: 0,
pending: None,
events_observed: 0,
evictions: 0,
emissions: 0,
};
for (at, event) in history.enumerate() {
reduce.ingest(&event, Place { at });
}
reduce.settle(false);
Ok(reduce)
}
pub(crate) fn ingest(&mut self, event: &LiveEvent, place: Place) {
self.events_observed += 1;
let index = self.next;
self.next += 1;
self.held.insert(place.at, index);
for (src, state) in &mut self.columns {
if let Some(v) = &event.values()[*src] {
state.add(index, v);
}
}
if matches!(self.trigger, TriggerKind::Count(_)) {
self.since_emit += 1;
}
self.pending = Some(event.key().clone());
}
pub(crate) fn evict_cells(&mut self, buffer: &EventBuffer, n: usize) {
for i in 0..n.min(buffer.len()) {
let Some(index) = self.held.pop_front() else {
return;
};
self.evictions += 1;
for (src, state) in &mut self.columns {
if let Some(v) = buffer.cell(*src, i) {
state.remove(index, &v);
}
}
}
}
pub(crate) fn settle(&mut self, hand_on: bool) -> Vec<LiveEvent> {
let Some(key) = self.pending.take() else {
return Vec::new();
};
let mut out = Vec::new();
match self.trigger {
TriggerKind::Event => out.extend(self.emit(key, hand_on)),
TriggerKind::Count(n) => {
while self.since_emit >= n {
self.since_emit -= n;
out.extend(self.emit(key.clone(), hand_on));
}
}
TriggerKind::Clock { step, anchor } => {
if let Some(start) = clock_crossed(&mut self.last_bucket, key.begin(), step, anchor)
{
out.extend(self.emit(EventKey::time(start), hand_on));
}
}
}
out
}
fn emit(&mut self, key: EventKey, hand_on: bool) -> Option<LiveEvent> {
self.emissions += 1;
if hand_on {
let values: Vec<Option<Value>> =
self.columns.iter_mut().map(|c| c.1.snapshot()).collect();
let event = LiveEvent::new(Arc::clone(&self.schema), key, values);
self.out.append_ref(&event);
Some(event)
} else {
self.out
.append_values(key, self.columns.iter_mut().map(|c| c.1.snapshot()));
None
}
}
pub(crate) fn has_pending(&self) -> bool {
self.pending.is_some()
}
pub(crate) fn schema_arc(&self) -> Arc<LiveSchema> {
Arc::clone(&self.schema)
}
pub(crate) fn kept(&self) -> &EventBuffer {
&self.out
}
pub fn name(&self) -> &str {
&self.name
}
pub fn schema(&self) -> &[ColumnDef] {
&self.schema.columns
}
pub fn len(&self) -> usize {
self.out.len()
}
pub fn is_empty(&self) -> bool {
self.out.len() == 0
}
pub fn at(&self, index: i64) -> Option<LiveEvent> {
self.out.at(index)
}
pub fn events(&self) -> impl ExactSizeIterator<Item = LiveEvent> + '_ {
self.out.iter()
}
pub fn value(&self) -> Vec<Option<Value>> {
self.columns.iter().map(|c| c.1.peek()).collect()
}
pub fn stats(&self) -> ReduceStats {
ReduceStats {
events_observed: self.events_observed,
evictions: self.evictions,
emissions: self.emissions,
buffer_size: self.events_observed - self.evictions,
}
}
}