use std::path::PathBuf;
use super::{
CdcBackend, CdcCapability, CdcKind, ChangeRequest, ChangeStream, change_row_schema,
encode_change_rows, fold_updates, post_filter,
};
use crate::error::{BifrostError, Result};
pub struct SkadeCdc {
warehouse_dir: PathBuf,
}
impl SkadeCdc {
pub fn new(dir: impl Into<PathBuf>) -> Self {
Self {
warehouse_dir: dir.into(),
}
}
}
#[async_trait::async_trait]
impl CdcBackend for SkadeCdc {
fn kind(&self) -> CdcKind {
CdcKind::SkadeDelta
}
async fn changes(&self, req: &ChangeRequest) -> Result<ChangeStream> {
let src = |e: skade::SkadeError| BifrostError::Source(format!("{}: {e}", req.table));
let wh = skade::Warehouse::open(&self.warehouse_dir)
.await
.map_err(src)?;
let table = wh.table(&req.table).await.map_err(src)?;
let to = match req.to_snapshot {
Some(v) => v,
None => match table.inner().metadata().current_snapshot() {
Some(s) => s.snapshot_id(),
None => {
return Ok(ChangeStream::ready(
change_row_schema(&arrow_schema::Schema::empty()),
CdcCapability::skade(),
Vec::new(),
));
}
},
};
let cl = table
.read_changelog(req.from_snapshot, to)
.await
.map_err(src)?;
let plan = cl.plan.clone();
let cl = if req.net_changes {
cl.collapsed().map_err(src)?
} else {
cl
};
let engine_cols = req.engine_columns();
let refs: Vec<&str> = engine_cols.iter().map(String::as_str).collect();
let cl = cl.project_columns(&refs).map_err(src)?;
let mut rows = Vec::with_capacity(cl.rows.len());
let mut schema = None;
for b in &cl.rows {
let b = match &req.filter {
Some(f) => post_filter(b, f)?,
None => b.clone(),
};
let enc = encode_change_rows(&b, Some(to))?;
let enc = fold_updates(&enc, &req.identity)?;
schema.get_or_insert_with(|| enc.schema());
rows.push(enc);
}
let schema = schema.unwrap_or_else(|| change_row_schema(&arrow_schema::Schema::empty()));
let total: usize = rows.iter().map(|b| b.num_rows()).sum();
tracing::info!(
target: "bifrost::cdc",
event = "cdc_read",
table = %req.table,
backend = CdcKind::SkadeDelta.as_str(),
from = ?req.from_snapshot,
to,
snapshots = plan.snapshots.len(),
added_files = plan.added_files.len(),
delete_files = plan.delete_files.len(),
full_reload = plan.needs_full_reload,
projected = req.columns.len(),
post_filtered = req.filter.is_some(),
change_rows = total,
batches = rows.len(),
"read change feed",
);
Ok(ChangeStream::ready(schema, CdcCapability::skade(), rows))
}
}