Skip to content

Commit 0ca25bb

Browse files
fix(function): isolated-vm worker pool to prevent single-worker bottleneck + execution user id resolution (#3155)
* fix(executor): isolated-vm worker pool to prevent single-worker bottleneck * chore(helm): add isolated-vm worker pool env vars to values.yaml * fix(userid): resolution for fair scheduling * add fallback back * add to helm charts * remove constant fallbacks * fix * address bugbot comments * fix fallbacks * one more bugbot comment --------- Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
1 parent 1edaf19 commit 0ca25bb

File tree

19 files changed

+1519
-206
lines changed

19 files changed

+1519
-206
lines changed

apps/sim/app/api/function/execute/route.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,8 @@ export async function POST(req: NextRequest) {
845845
contextVariables,
846846
timeoutMs: timeout,
847847
requestId,
848+
ownerKey: `user:${auth.userId}`,
849+
ownerWeight: 1,
848850
})
849851

850852
const executionTime = Date.now() - startTime

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,11 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
325325
requestId
326326
)
327327

328+
// Client-side sessions and personal API keys bill/permission-check the
329+
// authenticated user, not the workspace billed account.
330+
const useAuthenticatedUserAsActor =
331+
isClientSession || (auth.authType === 'api_key' && auth.apiKeyType === 'personal')
332+
328333
const preprocessResult = await preprocessExecution({
329334
workflowId,
330335
userId,
@@ -334,6 +339,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
334339
checkDeployment: !shouldUseDraftState,
335340
loggingSession,
336341
useDraftState: shouldUseDraftState,
342+
useAuthenticatedUserAsActor,
337343
})
338344

339345
if (!preprocessResult.success) {

apps/sim/executor/handlers/agent/agent-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ export class AgentBlockHandler implements BlockHandler {
326326
_context: {
327327
workflowId: ctx.workflowId,
328328
workspaceId: ctx.workspaceId,
329+
userId: ctx.userId,
329330
isDeployedContext: ctx.isDeployedContext,
330331
},
331332
},

apps/sim/executor/handlers/api/api-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export class ApiBlockHandler implements BlockHandler {
7272
workflowId: ctx.workflowId,
7373
workspaceId: ctx.workspaceId,
7474
executionId: ctx.executionId,
75+
userId: ctx.userId,
7576
isDeployedContext: ctx.isDeployedContext,
7677
},
7778
},

apps/sim/executor/handlers/condition/condition-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export async function evaluateConditionExpression(
4848
_context: {
4949
workflowId: ctx.workflowId,
5050
workspaceId: ctx.workspaceId,
51+
userId: ctx.userId,
5152
isDeployedContext: ctx.isDeployedContext,
5253
},
5354
},

apps/sim/executor/handlers/function/function-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export class FunctionBlockHandler implements BlockHandler {
3939
_context: {
4040
workflowId: ctx.workflowId,
4141
workspaceId: ctx.workspaceId,
42+
userId: ctx.userId,
4243
isDeployedContext: ctx.isDeployedContext,
4344
},
4445
},

apps/sim/executor/handlers/generic/generic-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export class GenericBlockHandler implements BlockHandler {
6666
workflowId: ctx.workflowId,
6767
workspaceId: ctx.workspaceId,
6868
executionId: ctx.executionId,
69+
userId: ctx.userId,
6970
isDeployedContext: ctx.isDeployedContext,
7071
},
7172
},

apps/sim/executor/handlers/human-in-the-loop/human-in-the-loop-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ export class HumanInTheLoopBlockHandler implements BlockHandler {
605605
_context: {
606606
workflowId: ctx.workflowId,
607607
workspaceId: ctx.workspaceId,
608+
userId: ctx.userId,
608609
isDeployedContext: ctx.isDeployedContext,
609610
},
610611
blockData: blockDataWithPause,

apps/sim/executor/orchestrators/loop.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,8 @@ export class LoopOrchestrator {
511511
contextVariables: {},
512512
timeoutMs: LOOP_CONDITION_TIMEOUT_MS,
513513
requestId,
514+
ownerKey: `user:${ctx.userId}`,
515+
ownerWeight: 1,
514516
})
515517

516518
if (vmResult.error) {

apps/sim/lib/auth/hybrid.ts

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import { db } from '@sim/db'
2-
import { workflow } from '@sim/db/schema'
31
import { createLogger } from '@sim/logger'
4-
import { eq } from 'drizzle-orm'
52
import type { NextRequest } from 'next/server'
63
import { authenticateApiKeyFromHeader, updateApiKeyLastUsed } from '@/lib/api-key/service'
74
import { getSession } from '@/lib/auth'
@@ -13,35 +10,33 @@ export interface AuthResult {
1310
success: boolean
1411
userId?: string
1512
authType?: 'session' | 'api_key' | 'internal_jwt'
13+
apiKeyType?: 'personal' | 'workspace'
1614
error?: string
1715
}
1816

1917
/**
2018
* Resolves userId from a verified internal JWT token.
21-
* Extracts workflowId/userId from URL params or POST body, then looks up userId if needed.
19+
* Extracts userId from the JWT payload, URL search params, or POST body.
2220
*/
2321
async function resolveUserFromJwt(
2422
request: NextRequest,
2523
verificationUserId: string | null,
2624
options: { requireWorkflowId?: boolean }
2725
): Promise<AuthResult> {
28-
let workflowId: string | null = null
2926
let userId: string | null = verificationUserId
3027

31-
const { searchParams } = new URL(request.url)
32-
workflowId = searchParams.get('workflowId')
3328
if (!userId) {
29+
const { searchParams } = new URL(request.url)
3430
userId = searchParams.get('userId')
3531
}
3632

37-
if (!workflowId && !userId && request.method === 'POST') {
33+
if (!userId && request.method === 'POST') {
3834
try {
3935
const clonedRequest = request.clone()
4036
const bodyText = await clonedRequest.text()
4137
if (bodyText) {
4238
const body = JSON.parse(bodyText)
43-
workflowId = body.workflowId || body._context?.workflowId
44-
userId = userId || body.userId || body._context?.userId
39+
userId = body.userId || body._context?.userId || null
4540
}
4641
} catch {
4742
// Ignore JSON parse errors
@@ -52,22 +47,8 @@ async function resolveUserFromJwt(
5247
return { success: true, userId, authType: 'internal_jwt' }
5348
}
5449

55-
if (workflowId) {
56-
const [workflowData] = await db
57-
.select({ userId: workflow.userId })
58-
.from(workflow)
59-
.where(eq(workflow.id, workflowId))
60-
.limit(1)
61-
62-
if (!workflowData) {
63-
return { success: false, error: 'Workflow not found' }
64-
}
65-
66-
return { success: true, userId: workflowData.userId, authType: 'internal_jwt' }
67-
}
68-
6950
if (options.requireWorkflowId !== false) {
70-
return { success: false, error: 'workflowId or userId required for internal JWT calls' }
51+
return { success: false, error: 'userId required for internal JWT calls' }
7152
}
7253

7354
return { success: true, authType: 'internal_jwt' }
@@ -222,6 +203,7 @@ export async function checkHybridAuth(
222203
success: true,
223204
userId: result.userId!,
224205
authType: 'api_key',
206+
apiKeyType: result.keyType,
225207
}
226208
}
227209

0 commit comments

Comments
 (0)