use nodedb_types::Surrogate;
use nodedb_types::sync::wire::SyncProvenance;
use nodedb_wal::record::RecordType;
use super::core_loop::CoreLoop;
use crate::data::executor::core_loop::write_index::KeyRepr;
use crate::engine::document::store::surrogate_to_doc_id;
impl CoreLoop {
pub fn replay_document_vector_wal(
&mut self,
records: &[nodedb_wal::WalRecord],
num_cores: usize,
tombstones: &nodedb_wal::TombstoneSet,
) {
let mut rebuilt = 0usize;
for record in records {
let rt = RecordType::from_raw(record.logical_record_type());
let is_put = rt == Some(RecordType::Put);
let is_delete = rt == Some(RecordType::Delete);
if !is_put && !is_delete {
continue;
}
let vshard_id = record.header.vshard_id as usize;
let target_core = if num_cores > 0 {
vshard_id % num_cores
} else {
0
};
if target_core != self.core_id {
continue;
}
let payload = &record.payload;
if is_delete {
let Ok((collection, _document_id, _prov, surrogate_u32)) =
zerompk::from_msgpack::<(String, String, Option<SyncProvenance>, u32)>(payload)
else {
continue;
};
let tenant_id = record.header.tenant_id;
if tombstones.is_tombstoned(
record.header.database_id,
tenant_id,
&collection,
record.header.lsn,
) {
continue;
}
let database_id = record.header.database_id;
let row_key = surrogate_to_doc_id(Surrogate::new(surrogate_u32));
self.remove_document_vector_indexes(database_id, tenant_id, &collection, &row_key);
let record_lsn = record.header.lsn;
self.note_replay_write_lsn(
database_id,
tenant_id,
&collection,
Some(KeyRepr::Surrogate(surrogate_u32)),
record_lsn,
);
continue;
}
if is_kv_put_record(payload) {
continue;
}
let Some((collection, value, surrogate)) = decode_document_put(payload) else {
continue;
};
let tenant_id = record.header.tenant_id;
let record_lsn = record.header.lsn;
if tombstones.is_tombstoned(
record.header.database_id,
tenant_id,
&collection,
record_lsn,
) {
continue;
}
let database_id = record.header.database_id;
let row_key = surrogate_to_doc_id(surrogate);
let deltas = self.apply_point_put_vector_indexes(
crate::data::executor::handlers::point::apply_put::VectorIndexPutParams {
database_id,
tid: tenant_id,
collection: &collection,
document_id: &row_key,
surrogate,
value: &value,
wal_lsn: record_lsn,
},
);
if !deltas.is_empty() {
rebuilt += deltas.len();
}
self.note_replay_write_lsn(
database_id,
tenant_id,
&collection,
Some(KeyRepr::Surrogate(surrogate.as_u32())),
record_lsn,
);
}
if rebuilt > 0 {
tracing::info!(
core = self.core_id,
rebuilt,
"WAL document vector-index replay complete"
);
}
}
}
fn is_kv_put_record(payload: &[u8]) -> bool {
if let Ok((disc, _c, _k, _v, _ttl)) =
zerompk::from_msgpack::<(&str, String, Vec<u8>, Vec<u8>, u64)>(payload)
&& disc == "kv_put"
{
return true;
}
if let Ok((disc, _c, _e, _ttl)) =
zerompk::from_msgpack::<(&str, String, Vec<(Vec<u8>, Vec<u8>)>, u64)>(payload)
&& disc == "kv_batch_put"
{
return true;
}
false
}
fn decode_document_put(payload: &[u8]) -> Option<(String, Vec<u8>, Surrogate)> {
if let Ok((collection, _document_id, value, _prov, surrogate_u32)) =
zerompk::from_msgpack::<(String, String, Vec<u8>, Option<SyncProvenance>, u32)>(payload)
{
return Some((collection, value, Surrogate::new(surrogate_u32)));
}
None
}