Matrix: ignore stale history after room eviction

This commit is contained in:
Gustavo Madeira Santana 2026-03-30 03:04:43 -04:00
parent e03327203c
commit 2b3d776403
No known key found for this signature in database
2 changed files with 32 additions and 1 deletions

View File

@ -125,4 +125,29 @@ describe("createRoomHistoryTracker — roomQueues eviction", () => {
expect(history).toHaveLength(1);
expect(history[0]?.body).toBe("new msg in room1");
});
it("ignores late consumeHistory calls after the room queue was evicted", () => {
const tracker = createRoomHistoryTracker(200, 1);
const room1 = "!room1:test";
const room2 = "!room2:test";
tracker.recordPending(room1, entry("old msg in room1"));
const prepared = tracker.prepareTrigger(AGENT, room1, 100, {
sender: "user",
body: "trigger in room1",
messageId: "$trigger",
});
// room2 creation evicts room1 (maxRoomQueues=1) while the trigger is still in flight.
tracker.recordPending(room2, entry("msg in room2"));
// Late completion for the evicted room must not recreate a stale watermark.
tracker.consumeHistory(AGENT, room1, prepared.snapshotIdx, "$trigger");
// Recreate room1 and add fresh content.
tracker.recordPending(room1, entry("new msg in room1"));
const history = tracker.getPendingHistory(AGENT, room1, 100);
expect(history).toHaveLength(1);
expect(history[0]?.body).toBe("new msg in room1");
});
});

View File

@ -198,11 +198,17 @@ export function createRoomHistoryTracker(
consumeHistory(agentId, roomId, snapshotIdx, messageId) {
const key = wmKey(agentId, roomId);
const queue = roomQueues.get(roomId);
if (!queue) {
// The room was evicted while this trigger was in flight. Keep eviction authoritative
// so a late completion cannot recreate a stale watermark against a fresh queue.
agentWatermarks.delete(key);
return;
}
// Monotone write: never regress an already-advanced watermark.
// Guards against out-of-order completion when two triggers for the same
// (agentId, roomId) are in-flight concurrently.
agentWatermarks.set(key, Math.max(agentWatermarks.get(key) ?? 0, snapshotIdx));
const queue = roomQueues.get(roomId);
const retryKey = preparedTriggerKey(agentId, messageId);
if (queue && retryKey) {
queue.preparedTriggers.delete(retryKey);