fix: per-handler claim lease in ledger, nats max_deliver, pin handler names

This commit is contained in:
2026-10-26 00:24:56 +01:00
parent 5681d0c034
commit 53892847f2
14 changed files with 489 additions and 118 deletions

View File

@@ -9,6 +9,13 @@ use crate::models::{
};
use crate::value_objects::SocialIdentity;
/// One `processed_events` row.
pub(super) struct LedgerRow {
pub(super) done: bool,
pub(super) started_at: chrono::NaiveDateTime,
pub(super) processed_at: chrono::NaiveDateTime,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum FollowState {
Pending,
@@ -64,7 +71,7 @@ pub struct InMemoryStore {
pub(super) import_profiles: RwLock<HashMap<Uuid, ImportProfile>>,
pub(super) follows: RwLock<Vec<(Uuid, SocialIdentity, FollowState)>>,
pub(super) blocked: RwLock<Vec<(Uuid, SocialIdentity)>>,
pub(super) processed_events: RwLock<HashMap<(Uuid, String), chrono::NaiveDateTime>>,
pub(super) processed_events: RwLock<HashMap<(Uuid, String), LedgerRow>>,
}
impl InMemoryStore {
@@ -128,17 +135,22 @@ impl InMemoryStore {
self.processed_events.read().unwrap().len()
}
/// Inserts a ledger row with an explicit timestamp (for retention tests).
/// Inserts a ledger row with explicit timestamps (retention and lease tests).
pub fn seed_processed_event(
&self,
id: &crate::events::EventId,
handler: &str,
processed_at: chrono::NaiveDateTime,
done: bool,
at: chrono::NaiveDateTime,
) {
self.processed_events
.write()
.unwrap()
.insert((id.value(), handler.to_owned()), processed_at);
self.processed_events.write().unwrap().insert(
(id.value(), handler.to_owned()),
LedgerRow {
done,
started_at: at,
processed_at: at,
},
);
}
pub fn refresh_session_count(&self) -> usize {

View File

@@ -1,33 +1,70 @@
use async_trait::async_trait;
use chrono::{Duration, NaiveDateTime, Utc};
use crate::{errors::DomainError, events::EventId, ports::ProcessedEventLedger};
use crate::{
errors::DomainError,
events::EventId,
ports::{Begin, HANDLER_LEASE_SECONDS, ProcessedEventLedger},
};
use super::InMemoryStore;
use super::store::LedgerRow;
/// Mirrors the sqlite adapter: `begin` inserts, or takes over only a stale
/// `in_progress` claim; `(event_id, handler)` is the key.
#[async_trait]
impl ProcessedEventLedger for InMemoryStore {
async fn has_processed(&self, id: &EventId, handler: &str) -> Result<bool, DomainError> {
Ok(self
.processed_events
.read()
.unwrap()
.contains_key(&(id.value(), handler.to_owned())))
async fn begin(&self, id: &EventId, handler: &str) -> Result<Begin, DomainError> {
let now = Utc::now().naive_utc();
let mut rows = self.processed_events.write().unwrap();
let key = (id.value(), handler.to_owned());
match rows.get_mut(&key) {
None => {
rows.insert(
key,
LedgerRow {
done: false,
started_at: now,
processed_at: now,
},
);
Ok(Begin::Run)
}
Some(row) if row.done => Ok(Begin::AlreadyDone),
Some(row) if row.started_at < now - Duration::seconds(HANDLER_LEASE_SECONDS) => {
row.started_at = now;
Ok(Begin::Run)
}
Some(_) => Ok(Begin::InProgressElsewhere),
}
}
/// `(event_id, handler)` is the primary key; a repeat keeps the first row.
async fn record_processed(&self, id: &EventId, handler: &str) -> Result<(), DomainError> {
self.processed_events
async fn complete(&self, id: &EventId, handler: &str) -> Result<(), DomainError> {
if let Some(row) = self
.processed_events
.write()
.unwrap()
.entry((id.value(), handler.to_owned()))
.or_insert_with(|| chrono::Utc::now().naive_utc());
.get_mut(&(id.value(), handler.to_owned()))
{
row.done = true;
row.processed_at = Utc::now().naive_utc();
}
Ok(())
}
async fn delete_older_than(&self, cutoff: chrono::NaiveDateTime) -> Result<u64, DomainError> {
async fn release(&self, id: &EventId, handler: &str) -> Result<(), DomainError> {
let mut rows = self.processed_events.write().unwrap();
let key = (id.value(), handler.to_owned());
if rows.get(&key).is_some_and(|r| !r.done) {
rows.remove(&key);
}
Ok(())
}
async fn delete_older_than(&self, cutoff: NaiveDateTime) -> Result<u64, DomainError> {
let mut rows = self.processed_events.write().unwrap();
let before = rows.len();
rows.retain(|_, at| *at >= cutoff);
rows.retain(|_, r| r.processed_at >= cutoff);
Ok((before - rows.len()) as u64)
}
}