feat(management): opportunistic expiry sweep on secured-write list (arch-review S2)

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
Joseph Doherty
2026-07-10 05:39:29 -04:00
parent 064a7d9415
commit 03295e91bb
3 changed files with 53 additions and 1 deletions
@@ -1395,6 +1395,22 @@ public class ManagementActor : ReceiveActor
{
var repo = sp.GetRequiredService<ISecuredWriteRepository>();
var rows = await repo.QueryAsync(cmd.Status, cmd.SiteId, skip: 0, take: 200);
// Opportunistic expiry sweep (arch-review S2): every time the list is read, walk
// the page and expire any overdue Pending row. TryExpireIfStaleAsync self-filters
// by age (fresh rows are no-ops) and CAS-guards the transition, so this is
// deliberately lazy/opportunistic — the ManagementActor already runs on every
// central node, so a dedicated expiry-timer singleton would be redundant, and the
// compare-and-swap makes two nodes sweeping the same overdue row concurrently
// harmless (exactly one wins; the loser observes false and re-projects the row's
// freshly-Expired state). The mutation lands on the in-memory row before projection,
// so the caller sees Expired in the same response.
foreach (var row in rows)
{
if (string.Equals(row.Status, "Pending", StringComparison.Ordinal))
await TryExpireIfStaleAsync(sp, repo, row);
}
return new SecuredWriteListResult(rows.Select(ToSecuredWriteDto).ToList());
}