killDeadClients

This commit is contained in:
Ben
2025-11-21 20:56:27 -05:00
parent 1b0f3f52d8
commit 63a1618f26
+12 -17
View File
@@ -22,36 +22,31 @@ export class ClientService {
// client.service.ts // client.service.ts
public async killDeadClients(): Promise<boolean> { public async killDeadClients(): Promise<boolean> {
const BATCH_SIZE = 500; const BATCH_SIZE = 10000;
return this.dataSource.transaction('REPEATABLE READ', async (manager) => { return this.dataSource.transaction('READ COMMITTED', async (manager) => { // ← CHANGE HERE
// IMPORTANT: We use raw SQL for the SELECT + SKIP LOCKED
// This bypasses every TypeORM locking bug with inheritance
const deadClients: { id: string }[] = await manager.query(` const deadClients: { id: string }[] = await manager.query(`
SELECT id SELECT id
FROM client_entity FROM client_entity
WHERE "deletedAt" IS NULL WHERE "deletedAt" IS NULL
AND "updatedAt" < NOW() - INTERVAL '5 minutes' AND "updatedAt" < NOW() - INTERVAL '5 minutes'
ORDER BY id ORDER BY id
LIMIT $1 LIMIT $1
FOR UPDATE SKIP LOCKED FOR UPDATE SKIP LOCKED
`, [BATCH_SIZE]); `, [BATCH_SIZE]);
if (deadClients.length === 0) { if (deadClients.length === 0) {
return false; return false;
} }
const ids = deadClients.map(c => c.id);
// Now the update is safe and super fast
await manager await manager
.createQueryBuilder() .createQueryBuilder()
.update(ClientEntity) .update(ClientEntity)
.set({ deletedAt: () => 'NOW()' }) .set({ deletedAt: () => 'NOW()' })
.whereInIds(ids) .whereInIds(deadClients.map(c => c.id))
.execute(); .execute();
console.log(`Killed ${deadClients.length}`); console.log(`Killed ${deadClients.length} dead clients`);
return true; return true;
}); });