use std::sync::Arc;
use super::{AppendResolution, DurableChannel, MessageEnvelope};
use crate::durability::head_checkpoint::READ_BATCH_SIZE;
use crate::durability::{DurabilityError, DurableStore};
#[derive(Debug)]
pub struct KeyLookup {
stream_keys: Vec<String>,
store: Arc<dyn DurableStore>,
}
impl DurableChannel {
#[must_use]
pub fn key_lookup(&self) -> KeyLookup {
KeyLookup {
stream_keys: (0..self.partition_count())
.map(|partition| self.stream_key_for(partition))
.collect(),
store: Arc::clone(&self.store),
}
}
pub async fn find_idempotency_key(
&self,
key: &str,
from: &[u64],
) -> Result<AppendResolution, DurabilityError> {
self.key_lookup().find(key, from).await
}
}
impl KeyLookup {
pub async fn find(&self, key: &str, from: &[u64]) -> Result<AppendResolution, DurabilityError> {
for (partition, stream_key) in self.stream_keys.iter().enumerate() {
let mut sequence = from.get(partition).copied().unwrap_or(0);
loop {
let page = self
.store
.read_from(stream_key, sequence, READ_BATCH_SIZE)
.await?;
for row in &page {
if row.sequence != sequence {
return Err(DurabilityError::ConfigError(format!(
"stream {stream_key} read at {sequence} returned sequence {}",
row.sequence
)));
}
let envelope = MessageEnvelope::deserialize(&row.payload)?;
if envelope.idempotency_key.as_deref() == Some(key) {
return Ok(AppendResolution::Committed {
position: row.sequence,
});
}
sequence = sequence.checked_add(1).ok_or_else(|| {
DurabilityError::ConfigError(format!(
"stream {stream_key} sequence overflow during key lookup"
))
})?;
}
if page.len() < READ_BATCH_SIZE {
break;
}
}
}
Ok(AppendResolution::Absent)
}
}