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:
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user