revise bulk-clear to explicit ids per mara's feedback, fix clippy line count
This commit is contained in:
parent
eb570c003d
commit
bc69ee3b8f
7 changed files with 228 additions and 181 deletions
|
|
@ -202,7 +202,7 @@ fn dispatch(
|
|||
} => clear_todo(store, &subsystem, key.as_deref(), all),
|
||||
Request::ListTodos { subsystem } => list_todos(store, subsystem.as_deref()),
|
||||
Request::MarkTodoDone { id } => mark_todo_done(store, id),
|
||||
Request::MarkTodosDoneUntil { up_to } => mark_todos_done_until(store, up_to),
|
||||
Request::MarkTodosDone { ids } => mark_todos_done(store, &ids),
|
||||
Request::StoreReminder {
|
||||
message,
|
||||
timing,
|
||||
|
|
@ -418,13 +418,13 @@ fn mark_todo_done(store: &Todos, id: i64) -> Response {
|
|||
}
|
||||
}
|
||||
|
||||
/// `MarkTodosDoneUntil` handler: bulk-acks every un-acked todo id at or
|
||||
/// below `up_to` — the escape hatch for a backlog too large to triage
|
||||
/// one-by-one (see `Todos::mark_done_until`'s doc for why it's needed).
|
||||
fn mark_todos_done_until(store: &Todos, up_to: i64) -> Response {
|
||||
match store.mark_done_until(up_to) {
|
||||
/// `MarkTodosDone` handler: bulk-acks an explicit list of todo ids — the
|
||||
/// escape hatch for a backlog too large to triage one-by-one (see
|
||||
/// `Todos::mark_done_many`'s doc for why it's ids, not a threshold).
|
||||
fn mark_todos_done(store: &Todos, ids: &[i64]) -> Response {
|
||||
match store.mark_done_many(ids) {
|
||||
Ok(count) => {
|
||||
tracing::debug!(up_to, count, "todo bulk mark-done");
|
||||
tracing::debug!(n_ids = ids.len(), count, "todo bulk mark-done");
|
||||
Response::Acked {
|
||||
count: u64::try_from(count).unwrap_or(0),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,17 +245,23 @@ impl Todos {
|
|||
Ok(n)
|
||||
}
|
||||
|
||||
/// Bulk-ack every un-acked todo with `id <= up_to` in one shot — the
|
||||
/// same "note the highest id you've seen, clear everything up to it"
|
||||
/// pattern the message inbox's `ack_until` already gives agents, applied
|
||||
/// to this store. Exists for the case a per-id triage loop isn't worth
|
||||
/// it: an agent that hasn't called `get_loose_ends` in a long stretch
|
||||
/// (or one whose producers pile up faster than it triages) can end up
|
||||
/// with a backlog large enough that the rendered list itself becomes
|
||||
/// unwieldy — clearing it in one call is the escape hatch, same
|
||||
/// `acked`-not-deleted semantics as [`Self::mark_done`] (a reconciled
|
||||
/// producer's next `upsert` still sees the row to compare against).
|
||||
/// Returns the number of rows newly acked.
|
||||
/// Bulk-ack a specific, explicit list of todo ids in one shot. Exists
|
||||
/// for the case a per-id `mark_done` loop isn't worth the round-trips:
|
||||
/// an agent that hasn't called `get_loose_ends` in a long stretch (or
|
||||
/// one whose producers pile up faster than it triages) can end up with
|
||||
/// a backlog large enough that clearing it one call at a time is
|
||||
/// impractical. Deliberately **explicit ids, not a `<= threshold`
|
||||
/// range** — a reviewer's call on the design: a range-based
|
||||
/// bulk-ack risks silently acking something the agent never actually
|
||||
/// looked at, since todos are heterogeneous unrelated items (bash /
|
||||
/// matrix / forge) rather than a sequentially-read stream the way inbox
|
||||
/// messages are. The caller is expected to have looked at each id
|
||||
/// (typically the ids `get_loose_ends` just rendered) before passing
|
||||
/// them here. Same `acked`-not-deleted semantics as [`Self::mark_done`]
|
||||
/// (a reconciled producer's next `upsert` still sees the row to compare
|
||||
/// against). Unknown/already-acked ids are silently skipped — same "not
|
||||
/// a new action" idempotence as the single-id path. Returns the number
|
||||
/// of rows newly acked.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
|
@ -264,13 +270,20 @@ impl Todos {
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if the connection mutex is poisoned.
|
||||
pub fn mark_done_until(&self, up_to: i64) -> Result<usize> {
|
||||
pub fn mark_done_many(&self, ids: &[i64]) -> Result<usize> {
|
||||
if ids.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let n = conn.execute(
|
||||
"UPDATE todos SET acked = 1, acked_at = ?1 WHERE acked = 0 AND id <= ?2",
|
||||
params![Utc::now().timestamp(), up_to],
|
||||
)?;
|
||||
Ok(n)
|
||||
let now = Utc::now().timestamp();
|
||||
let mut total = 0usize;
|
||||
for id in ids {
|
||||
total += conn.execute(
|
||||
"UPDATE todos SET acked = 1, acked_at = ?1 WHERE acked = 0 AND id = ?2",
|
||||
params![now, id],
|
||||
)?;
|
||||
}
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
/// List todos, newest-updated first. `subsystem = Some(..)` filters to
|
||||
|
|
@ -504,25 +517,26 @@ mod tests {
|
|||
assert!(!s.has_any().unwrap(), "acked-only table reads as empty");
|
||||
}
|
||||
|
||||
/// `mark_done_until` acks everything at-or-below the threshold id and
|
||||
/// leaves later rows untouched, mirroring `ack_until`'s inbox semantics.
|
||||
/// `mark_done_many` acks exactly the ids passed, leaves the rest
|
||||
/// untouched — no threshold/range semantics.
|
||||
#[test]
|
||||
fn mark_done_until_acks_up_to_threshold_only() {
|
||||
fn mark_done_many_acks_only_the_listed_ids() {
|
||||
let (_dir, s) = store();
|
||||
let (id1, _) = s.upsert("bash", None, "task 1", None).unwrap();
|
||||
let (id2, _) = s.upsert("bash", None, "task 2", None).unwrap();
|
||||
let (id3, _) = s.upsert("bash", None, "task 3", None).unwrap();
|
||||
assert_eq!(
|
||||
s.mark_done_until(id2).unwrap(),
|
||||
s.mark_done_many(&[id1, id3]).unwrap(),
|
||||
2,
|
||||
"acks id1 and id2, not id3"
|
||||
"acks id1 and id3, not id2 — not a range"
|
||||
);
|
||||
let left = s.list(None).unwrap();
|
||||
assert_eq!(left.len(), 1);
|
||||
assert_eq!(left[0].id, id3);
|
||||
// Idempotent: re-running over the same range acks nothing new.
|
||||
assert_eq!(s.mark_done_until(id2).unwrap(), 0);
|
||||
let _ = id1;
|
||||
assert_eq!(left[0].id, id2);
|
||||
// Idempotent: re-running over the same ids acks nothing new.
|
||||
assert_eq!(s.mark_done_many(&[id1, id3]).unwrap(), 0);
|
||||
// Empty input is a no-op, not an error.
|
||||
assert_eq!(s.mark_done_many(&[]).unwrap(), 0);
|
||||
}
|
||||
|
||||
/// `reap_acked` only removes acked rows past the cutoff — a recent ack
|
||||
|
|
|
|||
Loading…
Reference in a new issue