Skip to content

Commit ae113f7

Browse files
committed
fix cleanup
1 parent a160c8e commit ae113f7

File tree

1 file changed

+35
-29
lines changed

1 file changed

+35
-29
lines changed

apps/sim/lib/core/idempotency/cleanup.ts

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { db } from '@sim/db'
22
import { idempotencyKey } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
4-
import { and, eq, lt } from 'drizzle-orm'
4+
import { and, count, inArray, like, lt, max, min, sql } from 'drizzle-orm'
55

66
const logger = createLogger('IdempotencyCleanup')
77

@@ -19,7 +19,8 @@ export interface CleanupOptions {
1919
batchSize?: number
2020

2121
/**
22-
* Specific namespace to clean up, or undefined to clean all namespaces
22+
* Specific namespace prefix to clean up (e.g., 'webhook', 'polling')
23+
* Keys are prefixed with namespace, so this filters by key prefix
2324
*/
2425
namespace?: string
2526
}
@@ -53,13 +54,17 @@ export async function cleanupExpiredIdempotencyKeys(
5354

5455
while (hasMore) {
5556
try {
57+
// Build where condition - filter by cutoff date and optionally by namespace prefix
5658
const whereCondition = namespace
57-
? and(lt(idempotencyKey.createdAt, cutoffDate), eq(idempotencyKey.namespace, namespace))
59+
? and(
60+
lt(idempotencyKey.createdAt, cutoffDate),
61+
like(idempotencyKey.key, `${namespace}:%`)
62+
)
5863
: lt(idempotencyKey.createdAt, cutoffDate)
5964

60-
// First, find IDs to delete with limit
65+
// Find keys to delete with limit
6166
const toDelete = await db
62-
.select({ key: idempotencyKey.key, namespace: idempotencyKey.namespace })
67+
.select({ key: idempotencyKey.key })
6368
.from(idempotencyKey)
6469
.where(whereCondition)
6570
.limit(batchSize)
@@ -68,14 +73,13 @@ export async function cleanupExpiredIdempotencyKeys(
6873
break
6974
}
7075

71-
// Delete the found records
76+
// Delete the found records by key
7277
const deleteResult = await db
7378
.delete(idempotencyKey)
7479
.where(
75-
and(
76-
...toDelete.map((item) =>
77-
and(eq(idempotencyKey.key, item.key), eq(idempotencyKey.namespace, item.namespace))
78-
)
80+
inArray(
81+
idempotencyKey.key,
82+
toDelete.map((item) => item.key)
7983
)
8084
)
8185
.returning({ key: idempotencyKey.key })
@@ -126,6 +130,7 @@ export async function cleanupExpiredIdempotencyKeys(
126130

127131
/**
128132
* Get statistics about idempotency key usage
133+
* Uses SQL aggregations to avoid loading all keys into memory
129134
*/
130135
export async function getIdempotencyKeyStats(): Promise<{
131136
totalKeys: number
@@ -134,34 +139,35 @@ export async function getIdempotencyKeyStats(): Promise<{
134139
newestKey: Date | null
135140
}> {
136141
try {
137-
const allKeys = await db
142+
// Get total count and date range in a single query
143+
const [statsResult] = await db
138144
.select({
139-
namespace: idempotencyKey.namespace,
140-
createdAt: idempotencyKey.createdAt,
145+
totalKeys: count(),
146+
oldestKey: min(idempotencyKey.createdAt),
147+
newestKey: max(idempotencyKey.createdAt),
141148
})
142149
.from(idempotencyKey)
143150

144-
const totalKeys = allKeys.length
145-
const keysByNamespace: Record<string, number> = {}
146-
let oldestKey: Date | null = null
147-
let newestKey: Date | null = null
148-
149-
for (const key of allKeys) {
150-
keysByNamespace[key.namespace] = (keysByNamespace[key.namespace] || 0) + 1
151+
// Get counts by namespace prefix using SQL substring
152+
// Extracts everything before the first ':' as the namespace
153+
const namespaceStats = await db
154+
.select({
155+
namespace: sql<string>`split_part(${idempotencyKey.key}, ':', 1)`.as('namespace'),
156+
count: count(),
157+
})
158+
.from(idempotencyKey)
159+
.groupBy(sql`split_part(${idempotencyKey.key}, ':', 1)`)
151160

152-
if (!oldestKey || key.createdAt < oldestKey) {
153-
oldestKey = key.createdAt
154-
}
155-
if (!newestKey || key.createdAt > newestKey) {
156-
newestKey = key.createdAt
157-
}
161+
const keysByNamespace: Record<string, number> = {}
162+
for (const row of namespaceStats) {
163+
keysByNamespace[row.namespace || 'unknown'] = row.count
158164
}
159165

160166
return {
161-
totalKeys,
167+
totalKeys: statsResult?.totalKeys ?? 0,
162168
keysByNamespace,
163-
oldestKey,
164-
newestKey,
169+
oldestKey: statsResult?.oldestKey ?? null,
170+
newestKey: statsResult?.newestKey ?? null,
165171
}
166172
} catch (error) {
167173
logger.error('Failed to get idempotency key stats:', error)

0 commit comments

Comments
 (0)