fix(api): honour default TTLs and expire stale job ledger rows (#2887)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-21 23:16:39 +02:00
1 parent 242ed3a934
commit 0b2306ec3d
52 files changed
+3381 -174

No files matched your search

@@ -15,6 +15,8 @@ import {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
import type {IWorkerService} from '@pkgs/worker/src/contracts/IWorkerService';
import {ms} from 'itty-time';
const MESSAGE_DELETE_BATCH_DELAY_MS = ms('30 seconds');
interface MessageDeleteBatchGroup {
logs: Array<GuildAuditLog>;
userId: UserID;
@@ -81,14 +83,15 @@ export class GuildAuditLogService {
}
async scheduleMessageDeleteBatchJob(guildId: GuildID): Promise<void> {
const runAt = new Date(Date.now() + ms('30 seconds'));
const batchWindow = Math.floor(Date.now() / MESSAGE_DELETE_BATCH_DELAY_MS);
await this.workerService.addJob(
'batchGuildAuditLogMessageDeletes',
{guildId: guildId.toString()},
{
jobKey: `batch-audit-log-message-deletes:${guildId}`,
runAt,
jobKey: `batch-audit-log-message-deletes:${guildId}:${batchWindow}`,
runAt: new Date((batchWindow + 2) * MESSAGE_DELETE_BATCH_DELAY_MS),
maxAttempts: 3,
skipLedger: true,
},
);
}
@@ -60,7 +60,7 @@ function createService(roleNames: Map<string, string> = new Map()) {
{addJob} as unknown as IWorkerService<WorkerTaskName>,
{dispatchGuild} as unknown as IGatewayService,
);
return {service, createAuditLog, batchDeleteAndCreateAuditLogs, getRole, dispatchGuild};
return {service, createAuditLog, batchDeleteAndCreateAuditLogs, getRole, dispatchGuild, addJob};
}
function overwrites(
@@ -376,3 +376,26 @@ describe('GuildAuditLogService.recordPermissionOverwriteDiff', () => {
expect(dispatchGuild).not.toHaveBeenCalled();
});
});
describe('GuildAuditLogService.scheduleMessageDeleteBatchJob', () => {
it('gives every delete in one 30 second window a single batch job that runs after the window closes, without a ledger row', async () => {
vi.useFakeTimers({toFake: ['Date']});
try {
const {service, addJob} = createService();
for (const at of ['2026-09-21T12:00:00.000Z', '2026-09-21T12:00:29.999Z', '2026-09-21T12:00:40.000Z']) {
vi.setSystemTime(new Date(at));
await service.scheduleMessageDeleteBatchJob(GUILD_ID);
}
const options = addJob.mock.calls.map((call) => call[2] as {jobKey: string; runAt: Date; skipLedger: boolean});
expect(options.every((option) => option.skipLedger)).toBe(true);
expect(options[0]!.jobKey).toBe(options[1]!.jobKey);
expect(options[2]!.jobKey).not.toBe(options[1]!.jobKey);
expect(options[0]!.runAt.getTime()).toBeGreaterThan(new Date('2026-09-21T12:00:29.999Z').getTime());
expect(options[1]!.runAt).toEqual(options[0]!.runAt);
expect(options[2]!.runAt).toEqual(new Date('2026-09-21T12:01:30.000Z'));
expect(options[2]!.runAt.getTime() - options[0]!.runAt.getTime()).toBe(30_000);
} finally {
vi.useRealTimers();
}
});
});