Skip to content

Commit 9a31c7d

Browse files
authored
improvement(processing): reduce redundant DB queries in execution preprocessing (#3320)
* improvement(processing): reduce redundant DB queries in execution preprocessing * improvement(processing): add defensive ID check for prefetched workflow record * improvement(processing): fix type safety in execution error logging Replace `as any` cast in non-SSE error path with proper `buildTraceSpans()` transformation, matching the SSE error path. Remove redundant `as any` cast in preprocessing.ts where the types already align. * improvement(processing): replace `as any` casts with proper types in logging - logger.ts: cast JSONB cost column to `WorkflowExecutionLog['cost']` instead of `any` in both `completeWorkflowExecution` and `getWorkflowExecution` - logger.ts: replace `(orgUsageBefore as any)?.toString?.()` with `String()` since COALESCE guarantees a non-null SQL aggregate value - logging-session.ts: cast JSONB cost to `AccumulatedCost` (the local interface) instead of `any` in `loadExistingCost` * improvement(processing): use exported HighestPrioritySubscription type in usage.ts Replace inline `Awaited<ReturnType<typeof getHighestPrioritySubscription>>` with the already-exported `HighestPrioritySubscription` type alias. * improvement(processing): replace remaining `as any` casts with proper types - preprocessing.ts: use exported `HighestPrioritySubscription` type instead of redeclaring via `Awaited<ReturnType<...>>` - deploy/route.ts, status/route.ts: cast `hasWorkflowChanged` args to `WorkflowState` instead of `any` (JSONB + object literal narrowing) - state/route.ts: type block sanitization and save with `BlockState` and `WorkflowState` instead of `any` - search-suggestions.ts: remove 8 unnecessary `as any` casts on `'date'` literal that already satisfies the `Suggestion['category']` union * fix(processing): prevent double-billing race in LoggingSession completion When executeWorkflowCore throws, its catch block fire-and-forgets safeCompleteWithError, then re-throws. The caller's catch block also fire-and-forgets safeCompleteWithError on the same LoggingSession. Both check this.completed (still false) before either's async DB write resolves, so both proceed to completeWorkflowExecution which uses additive SQL for billing — doubling the charged cost on every failed execution. Fix: add a synchronous `completing` flag set immediately before the async work begins. This blocks concurrent callers at the guard check. On failure, the flag is reset so the safe* fallback path (completeWithCostOnlyLog) can still attempt recovery. * fix(processing): unblock error responses and isolate run-count failures Remove unnecessary `await waitForCompletion()` from non-SSE and SSE error paths where no `markAsFailed()` follows — these were blocking error responses on log persistence for no reason. Wrap `updateWorkflowRunCounts` in its own try/catch so a run-count DB failure cannot prevent session completion, billing, and trace span persistence. * improvement(processing): remove dead setupExecutor method The method body was just a debug log with an `any` parameter — logging now works entirely through trace spans with no executor integration. * remove logger.debug * fix(processing): guard completionPromise as write-once (singleton promise) Prevent concurrent safeComplete* calls from overwriting completionPromise with a no-op. The guard now lives at the assignment site — if a completion is already in-flight, return its promise instead of starting a new one. This ensures waitForCompletion() always awaits the real work. * improvement(processing): remove empty else/catch blocks left by debug log cleanup * fix(processing): enforce waitForCompletion inside markAsFailed to prevent completion races Move waitForCompletion() into markAsFailed() so every call site is automatically safe against in-flight fire-and-forget completions. Remove the now-redundant external waitForCompletion() calls in route.ts. * fix(processing): reset completing flag on fallback failure, clean up empty catch - completeWithCostOnlyLog now resets this.completing = false when the fallback itself fails, preventing a permanently stuck session - Use _disconnectError in MCP test-connection to signal intentional ignore * fix(processing): restore disconnect error logging in MCP test-connection Revert unrelated debug log removal — this file isn't part of the processing improvements and the log aids connection leak detection. * fix(processing): address audit findings across branch - preprocessing.ts: use undefined (not null) for failed subscription fetch so getUserUsageLimit does a fresh lookup instead of silently falling back to free-tier limits - deployed/route.ts: log warning on loadDeployedWorkflowState failure instead of silently swallowing the error - schedule-execution.ts: remove dead successLog parameter and all call-site arguments left over from logger.debug cleanup - mcp/middleware.ts: drop unused error binding in empty catch - audit/log.ts, wand.ts: promote logger.debug to logger.warn in catch blocks where these are the only failure signal * revert: undo unnecessary subscription null→undefined change getHighestPrioritySubscription never throws (it catches internally and returns null), so the catch block in preprocessExecution is dead code. The null vs undefined distinction doesn't matter and the coercions added unnecessary complexity. * improvement(processing): remove dead try/catch around getHighestPrioritySubscription getHighestPrioritySubscription catches internally and returns null on error, so the wrapping try/catch was unreachable dead code. * improvement(processing): remove dead getSnapshotByHash method No longer called after createSnapshotWithDeduplication was refactored to use a single upsert instead of select-then-insert. ---------
1 parent 9e817bc commit 9a31c7d

54 files changed

Lines changed: 335 additions & 614 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/sim/app/api/billing/update-cost/route.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export async function POST(req: NextRequest) {
3333
logger.info(`[${requestId}] Update cost request started`)
3434

3535
if (!isBillingEnabled) {
36-
logger.debug(`[${requestId}] Billing is disabled, skipping cost update`)
3736
return NextResponse.json({
3837
success: true,
3938
message: 'Billing disabled, cost update skipped',

apps/sim/app/api/chat/[identifier]/otp/route.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ export async function POST(
117117
const requestId = generateRequestId()
118118

119119
try {
120-
logger.debug(`[${requestId}] Processing OTP request for identifier: ${identifier}`)
121-
122120
const body = await request.json()
123121
const { email } = otpRequestSchema.parse(body)
124122

@@ -211,8 +209,6 @@ export async function PUT(
211209
const requestId = generateRequestId()
212210

213211
try {
214-
logger.debug(`[${requestId}] Verifying OTP for identifier: ${identifier}`)
215-
216212
const body = await request.json()
217213
const { email, otp } = otpVerifySchema.parse(body)
218214

apps/sim/app/api/chat/[identifier]/route.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ export async function POST(
4242
const requestId = generateRequestId()
4343

4444
try {
45-
logger.debug(`[${requestId}] Processing chat request for identifier: ${identifier}`)
46-
4745
let parsedBody
4846
try {
4947
const rawBody = await request.json()
@@ -294,8 +292,6 @@ export async function GET(
294292
const requestId = generateRequestId()
295293

296294
try {
297-
logger.debug(`[${requestId}] Fetching chat info for identifier: ${identifier}`)
298-
299295
const deploymentResult = await db
300296
.select({
301297
id: chat.id,

apps/sim/app/api/creators/route.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,6 @@ export async function POST(request: NextRequest) {
9595
const body = await request.json()
9696
const data = CreateCreatorProfileSchema.parse(body)
9797

98-
logger.debug(`[${requestId}] Creating creator profile:`, {
99-
referenceType: data.referenceType,
100-
referenceId: data.referenceId,
101-
})
102-
10398
// Validate permissions
10499
if (data.referenceType === 'user') {
105100
if (data.referenceId !== session.user.id) {

apps/sim/app/api/form/[identifier]/route.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ export async function POST(
5858
const requestId = generateRequestId()
5959

6060
try {
61-
logger.debug(`[${requestId}] Processing form submission for identifier: ${identifier}`)
62-
6361
let parsedBody
6462
try {
6563
const rawBody = await request.json()
@@ -300,8 +298,6 @@ export async function GET(
300298
const requestId = generateRequestId()
301299

302300
try {
303-
logger.debug(`[${requestId}] Fetching form info for identifier: ${identifier}`)
304-
305301
const deploymentResult = await db
306302
.select({
307303
id: form.id,

apps/sim/app/api/help/route.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ export async function POST(req: NextRequest) {
7777
}
7878
}
7979

80-
logger.debug(`[${requestId}] Help request includes ${images.length} images`)
81-
8280
const userId = session.user.id
8381
let emailText = `
8482
Type: ${type}

apps/sim/app/api/knowledge/search/route.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,6 @@ export async function POST(request: NextRequest) {
186186
valueTo: filter.valueTo,
187187
}
188188
})
189-
190-
logger.debug(`[${requestId}] Processed ${structuredFilters.length} structured filters`)
191189
}
192190

193191
if (accessibleKbIds.length === 0) {
@@ -220,7 +218,6 @@ export async function POST(request: NextRequest) {
220218

221219
if (!hasQuery && hasFilters) {
222220
// Tag-only search without vector similarity
223-
logger.debug(`[${requestId}] Executing tag-only search with filters:`, structuredFilters)
224221
results = await handleTagOnlySearch({
225222
knowledgeBaseIds: accessibleKbIds,
226223
topK: validatedData.topK,
@@ -244,7 +241,6 @@ export async function POST(request: NextRequest) {
244241
})
245242
} else if (hasQuery && !hasFilters) {
246243
// Vector-only search
247-
logger.debug(`[${requestId}] Executing vector-only search`)
248244
const strategy = getQueryStrategy(accessibleKbIds.length, validatedData.topK)
249245
const queryVector = JSON.stringify(await queryEmbeddingPromise)
250246

apps/sim/app/api/knowledge/search/utils.ts

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import { db } from '@sim/db'
22
import { document, embedding } from '@sim/db/schema'
3-
import { createLogger } from '@sim/logger'
43
import { and, eq, inArray, isNull, sql } from 'drizzle-orm'
54
import type { StructuredFilter } from '@/lib/knowledge/types'
65

7-
const logger = createLogger('KnowledgeSearchUtils')
8-
96
export async function getDocumentNamesByIds(
107
documentIds: string[]
118
): Promise<Record<string, string>> {
@@ -140,17 +137,12 @@ function buildFilterCondition(filter: StructuredFilter, embeddingTable: any) {
140137
const { tagSlot, fieldType, operator, value, valueTo } = filter
141138

142139
if (!isTagSlotKey(tagSlot)) {
143-
logger.debug(`[getStructuredTagFilters] Unknown tag slot: ${tagSlot}`)
144140
return null
145141
}
146142

147143
const column = embeddingTable[tagSlot]
148144
if (!column) return null
149145

150-
logger.debug(
151-
`[getStructuredTagFilters] Processing ${tagSlot} (${fieldType}) ${operator} ${value}`
152-
)
153-
154146
// Handle text operators
155147
if (fieldType === 'text') {
156148
const stringValue = String(value)
@@ -208,7 +200,6 @@ function buildFilterCondition(filter: StructuredFilter, embeddingTable: any) {
208200
const dateStr = String(value)
209201
// Validate YYYY-MM-DD format
210202
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
211-
logger.debug(`[getStructuredTagFilters] Invalid date format: ${dateStr}, expected YYYY-MM-DD`)
212203
return null
213204
}
214205

@@ -287,9 +278,6 @@ function getStructuredTagFilters(filters: StructuredFilter[], embeddingTable: an
287278
conditions.push(slotConditions[0])
288279
} else {
289280
// Multiple conditions for same slot - OR them together
290-
logger.debug(
291-
`[getStructuredTagFilters] OR'ing ${slotConditions.length} conditions for ${slot}`
292-
)
293281
conditions.push(sql`(${sql.join(slotConditions, sql` OR `)})`)
294282
}
295283
}
@@ -380,8 +368,6 @@ export async function handleTagOnlySearch(params: SearchParams): Promise<SearchR
380368
throw new Error('Tag filters are required for tag-only search')
381369
}
382370

383-
logger.debug(`[handleTagOnlySearch] Executing tag-only search with filters:`, structuredFilters)
384-
385371
const strategy = getQueryStrategy(knowledgeBaseIds.length, topK)
386372
const tagFilterConditions = getStructuredTagFilters(structuredFilters, embedding)
387373

@@ -431,8 +417,6 @@ export async function handleVectorOnlySearch(params: SearchParams): Promise<Sear
431417
throw new Error('Query vector and distance threshold are required for vector-only search')
432418
}
433419

434-
logger.debug(`[handleVectorOnlySearch] Executing vector-only search`)
435-
436420
const strategy = getQueryStrategy(knowledgeBaseIds.length, topK)
437421

438422
const distanceExpr = sql<number>`${embedding.embedding} <=> ${queryVector}::vector`.as('distance')
@@ -489,23 +473,13 @@ export async function handleTagAndVectorSearch(params: SearchParams): Promise<Se
489473
throw new Error('Query vector and distance threshold are required for tag and vector search')
490474
}
491475

492-
logger.debug(
493-
`[handleTagAndVectorSearch] Executing tag + vector search with filters:`,
494-
structuredFilters
495-
)
496-
497476
// Step 1: Filter by tags first
498477
const tagFilteredIds = await executeTagFilterQuery(knowledgeBaseIds, structuredFilters)
499478

500479
if (tagFilteredIds.length === 0) {
501-
logger.debug(`[handleTagAndVectorSearch] No results found after tag filtering`)
502480
return []
503481
}
504482

505-
logger.debug(
506-
`[handleTagAndVectorSearch] Found ${tagFilteredIds.length} results after tag filtering`
507-
)
508-
509483
// Step 2: Perform vector search only on tag-filtered results
510484
return await executeVectorSearchOnIds(
511485
tagFilteredIds.map((r) => r.id),

apps/sim/app/api/logs/execution/[executionId]/route.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ export async function GET(
3434

3535
const authenticatedUserId = authResult.userId
3636

37-
logger.debug(
38-
`[${requestId}] Fetching execution data for: ${executionId} (auth: ${authResult.authType})`
39-
)
40-
4137
const [workflowLog] = await db
4238
.select({
4339
id: workflowExecutionLogs.id,
@@ -125,11 +121,6 @@ export async function GET(
125121
},
126122
}
127123

128-
logger.debug(`[${requestId}] Successfully fetched execution data for: ${executionId}`)
129-
logger.debug(
130-
`[${requestId}] Workflow state contains ${Object.keys((snapshot.stateData as any)?.blocks || {}).length} blocks`
131-
)
132-
133124
return NextResponse.json(response)
134125
} catch (error) {
135126
logger.error(`[${requestId}] Error fetching execution data:`, error)

apps/sim/app/api/mcp/tools/execute/route.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ export const POST = withMcpAuth('read')(
8383
serverId: serverId,
8484
serverName: 'provided-schema',
8585
} as McpTool
86-
logger.debug(`[${requestId}] Using provided schema for ${toolName}, skipping discovery`)
8786
} else {
8887
const tools = await mcpService.discoverServerTools(userId, serverId, workspaceId)
8988
tool = tools.find((t) => t.name === toolName) ?? null

0 commit comments

Comments
 (0)