1.1.00
All checks were successful
Build and Release / build-and-release (push) Successful in 20m36s

This commit is contained in:
Bryan1029384756
2026-04-16 20:14:27 -05:00
parent 56a12fdf3e
commit 6813bb40dc
40 changed files with 2228 additions and 387 deletions

View File

@@ -22,6 +22,11 @@ export const startTyping = mutation({
const userTyping = existing.find((t) => t.userId === args.userId);
if (userTyping) {
// Refreshing an existing row — the cleanup scheduled from the
// original insert is still pending, so don't pile another copy
// onto the scheduler. The audit flagged the old code (schedule
// on every heartbeat) as spamming cleanExpired tasks that all
// did the same work.
await ctx.db.patch(userTyping._id, { expiresAt });
} else {
await ctx.db.insert("typingIndicators", {
@@ -30,9 +35,9 @@ export const startTyping = mutation({
username: args.username,
expiresAt,
});
await ctx.scheduler.runAfter(TYPING_TTL_MS, internal.typing.cleanExpired, {});
}
await ctx.scheduler.runAfter(TYPING_TTL_MS, internal.typing.cleanExpired, {});
return null;
},
});
@@ -93,11 +98,15 @@ export const cleanExpired = internalMutation({
returns: v.null(),
handler: async (ctx) => {
const now = Date.now();
const expired = await ctx.db.query("typingIndicators").collect();
// Range-scan the by_expires_at index instead of collecting the whole
// table, so cleanup cost scales with number of *expired* rows rather
// than total typing activity across every channel.
const expired = await ctx.db
.query("typingIndicators")
.withIndex("by_expires_at", (q) => q.lte("expiresAt", now))
.collect();
for (const t of expired) {
if (t.expiresAt <= now) {
await ctx.db.delete(t._id);
}
await ctx.db.delete(t._id);
}
return null;
},