fix(log): log cleanup sql query (#4087)

* fix(log): log cleanup sql query

* perf(log): use startedAt index for cleanup query filter

Switch cleanup WHERE clause from createdAt to startedAt to leverage
the existing composite index (workspaceId, startedAt), converting a
full table scan to an index range scan. Also remove explanatory comment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Theodore Li <theo@sim.ai>
Co-authored-by: Waleed Latif <walif6@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Theodore Li
2026-04-09 18:04:15 -07:00
committed by Waleed Latif
parent cb470ce8f2
commit 46cc5269d6

View File

@@ -1,5 +1,5 @@
import { db } from '@sim/db'
import { subscription, user, workflowExecutionLogs, workspace } from '@sim/db/schema'
import { subscription, workflowExecutionLogs, workspace } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, inArray, isNull, lt } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
@@ -26,38 +26,19 @@ export async function GET(request: NextRequest) {
const retentionDate = new Date()
retentionDate.setDate(retentionDate.getDate() - Number(env.FREE_PLAN_LOG_RETENTION_DAYS || '7'))
const freeUsers = await db
.select({ userId: user.id })
.from(user)
const freeWorkspacesSubquery = db
.select({ id: workspace.id })
.from(workspace)
.leftJoin(
subscription,
and(
eq(user.id, subscription.referenceId),
eq(subscription.referenceId, workspace.billedAccountUserId),
inArray(subscription.status, ENTITLED_SUBSCRIPTION_STATUSES),
sqlIsPaid(subscription.plan)
)
)
.where(isNull(subscription.id))
if (freeUsers.length === 0) {
logger.info('No free users found for log cleanup')
return NextResponse.json({ message: 'No free users found for cleanup' })
}
const freeUserIds = freeUsers.map((u) => u.userId)
const workspacesQuery = await db
.select({ id: workspace.id })
.from(workspace)
.where(inArray(workspace.billedAccountUserId, freeUserIds))
if (workspacesQuery.length === 0) {
logger.info('No workspaces found for free users')
return NextResponse.json({ message: 'No workspaces found for cleanup' })
}
const workspaceIds = workspacesQuery.map((w) => w.id)
const results = {
enhancedLogs: {
total: 0,
@@ -83,7 +64,7 @@ export async function GET(request: NextRequest) {
let batchesProcessed = 0
let hasMoreLogs = true
logger.info(`Starting enhanced logs cleanup for ${workspaceIds.length} workspaces`)
logger.info('Starting enhanced logs cleanup for free-plan workspaces')
while (hasMoreLogs && batchesProcessed < MAX_BATCHES) {
const oldEnhancedLogs = await db
@@ -105,8 +86,8 @@ export async function GET(request: NextRequest) {
.from(workflowExecutionLogs)
.where(
and(
inArray(workflowExecutionLogs.workspaceId, workspaceIds),
lt(workflowExecutionLogs.createdAt, retentionDate)
inArray(workflowExecutionLogs.workspaceId, freeWorkspacesSubquery),
lt(workflowExecutionLogs.startedAt, retentionDate)
)
)
.limit(BATCH_SIZE)