fix fallbacks

This commit is contained in:
Vikhyath Mondreti
2026-02-06 16:35:20 -08:00
parent ba6a01aebc
commit 0209d331f4
2 changed files with 39 additions and 36 deletions

View File

@@ -247,10 +247,10 @@ describe('isolated-vm scheduler', () => {
ownerKey: 'user:b',
})
expect(second.error?.name).toBe('QueueFullError')
expect(second.error?.message).toContain('at capacity')
const first = await firstPromise
expect(first.error?.name).toBe('QueueTimeoutError')
expect(first.error?.message).toContain('timed out waiting')
})
it('enforces per-owner queued limit', async () => {
@@ -284,10 +284,10 @@ describe('isolated-vm scheduler', () => {
ownerKey: 'user:hog',
})
expect(second.error?.name).toBe('OwnerQueueLimitError')
expect(second.error?.message).toContain('Too many concurrent')
const first = await firstPromise
expect(first.error?.name).toBe('QueueTimeoutError')
expect(first.error?.message).toContain('timed out waiting')
})
it('enforces distributed owner in-flight lease limit when Redis is configured', async () => {
@@ -316,7 +316,7 @@ describe('isolated-vm scheduler', () => {
ownerKey: 'user:distributed',
})
expect(result.error?.name).toBe('OwnerInFlightLimitError')
expect(result.error?.message).toContain('Too many concurrent')
})
it('fails closed when Redis is configured but unavailable', async () => {
@@ -337,7 +337,7 @@ describe('isolated-vm scheduler', () => {
ownerKey: 'user:redis-down',
})
expect(result.error?.name).toBe('DistributedFairnessUnavailableError')
expect(result.error?.message).toContain('temporarily unavailable')
})
it('fails closed when Redis lease evaluation errors', async () => {
@@ -365,7 +365,7 @@ describe('isolated-vm scheduler', () => {
ownerKey: 'user:redis-error',
})
expect(result.error?.name).toBe('DistributedFairnessUnavailableError')
expect(result.error?.message).toContain('temporarily unavailable')
})
it('applies weighted owner scheduling when draining queued executions', async () => {

View File

@@ -52,23 +52,24 @@ export interface IsolatedVMError {
lineContent?: string
}
const POOL_SIZE = Number.parseInt(env.IVM_POOL_SIZE)
const MAX_CONCURRENT = Number.parseInt(env.IVM_MAX_CONCURRENT)
const MAX_PER_WORKER = Number.parseInt(env.IVM_MAX_PER_WORKER)
const WORKER_IDLE_TIMEOUT_MS = Number.parseInt(env.IVM_WORKER_IDLE_TIMEOUT_MS)
const QUEUE_TIMEOUT_MS = Number.parseInt(env.IVM_QUEUE_TIMEOUT_MS)
const MAX_QUEUE_SIZE = Number.parseInt(env.IVM_MAX_QUEUE_SIZE)
const MAX_FETCH_RESPONSE_BYTES = Number.parseInt(env.IVM_MAX_FETCH_RESPONSE_BYTES)
const MAX_FETCH_RESPONSE_CHARS = Number.parseInt(env.IVM_MAX_FETCH_RESPONSE_CHARS)
const MAX_FETCH_URL_LENGTH = Number.parseInt(env.IVM_MAX_FETCH_URL_LENGTH)
const MAX_FETCH_OPTIONS_JSON_CHARS = Number.parseInt(env.IVM_MAX_FETCH_OPTIONS_JSON_CHARS)
const MAX_ACTIVE_PER_OWNER = Number.parseInt(env.IVM_MAX_ACTIVE_PER_OWNER)
const MAX_QUEUED_PER_OWNER = Number.parseInt(env.IVM_MAX_QUEUED_PER_OWNER)
const MAX_OWNER_WEIGHT = Number.parseInt(env.IVM_MAX_OWNER_WEIGHT)
const DISTRIBUTED_MAX_INFLIGHT_PER_OWNER = Number.parseInt(
env.IVM_DISTRIBUTED_MAX_INFLIGHT_PER_OWNER
)
const DISTRIBUTED_LEASE_MIN_TTL_MS = Number.parseInt(env.IVM_DISTRIBUTED_LEASE_MIN_TTL_MS)
const POOL_SIZE = Number.parseInt(env.IVM_POOL_SIZE) || 4
const MAX_CONCURRENT = Number.parseInt(env.IVM_MAX_CONCURRENT) || 10000
const MAX_PER_WORKER = Number.parseInt(env.IVM_MAX_PER_WORKER) || 2500
const WORKER_IDLE_TIMEOUT_MS = Number.parseInt(env.IVM_WORKER_IDLE_TIMEOUT_MS) || 60000
const QUEUE_TIMEOUT_MS = Number.parseInt(env.IVM_QUEUE_TIMEOUT_MS) || 300000
const MAX_QUEUE_SIZE = Number.parseInt(env.IVM_MAX_QUEUE_SIZE) || 10000
const MAX_FETCH_RESPONSE_BYTES = Number.parseInt(env.IVM_MAX_FETCH_RESPONSE_BYTES) || 8_388_608
const MAX_FETCH_RESPONSE_CHARS = Number.parseInt(env.IVM_MAX_FETCH_RESPONSE_CHARS) || 4_000_000
const MAX_FETCH_URL_LENGTH = Number.parseInt(env.IVM_MAX_FETCH_URL_LENGTH) || 8192
const MAX_FETCH_OPTIONS_JSON_CHARS =
Number.parseInt(env.IVM_MAX_FETCH_OPTIONS_JSON_CHARS) || 262_144
const MAX_ACTIVE_PER_OWNER = Number.parseInt(env.IVM_MAX_ACTIVE_PER_OWNER) || 200
const MAX_QUEUED_PER_OWNER = Number.parseInt(env.IVM_MAX_QUEUED_PER_OWNER) || 2000
const MAX_OWNER_WEIGHT = Number.parseInt(env.IVM_MAX_OWNER_WEIGHT) || 5
const DISTRIBUTED_MAX_INFLIGHT_PER_OWNER =
Number.parseInt(env.IVM_DISTRIBUTED_MAX_INFLIGHT_PER_OWNER) ||
MAX_ACTIVE_PER_OWNER + MAX_QUEUED_PER_OWNER
const DISTRIBUTED_LEASE_MIN_TTL_MS = Number.parseInt(env.IVM_DISTRIBUTED_LEASE_MIN_TTL_MS) || 120000
const DISTRIBUTED_KEY_PREFIX = 'ivm:fair:v1:owner'
const QUEUE_RETRY_DELAY_MS = 1000
const DISTRIBUTED_LEASE_GRACE_MS = 30000
@@ -611,7 +612,7 @@ function cleanupWorker(workerId: number) {
pending.resolve({
result: null,
stdout: '',
error: { message: 'Worker process exited unexpectedly', name: 'WorkerError' },
error: { message: 'Code execution failed unexpectedly. Please try again.', name: 'Error' },
})
workerInfo.pendingExecutions.delete(id)
}
@@ -848,7 +849,7 @@ function dispatchToWorker(
resolve({
result: null,
stdout: '',
error: { message: 'Failed to send execution request to worker', name: 'WorkerError' },
error: { message: 'Code execution failed to start. Please try again.', name: 'Error' },
})
resetWorkerIdleTimeout(workerInfo.id)
// Defer to break synchronous recursion: drainQueue → dispatchToWorker → catch → drainQueue
@@ -866,8 +867,8 @@ function enqueueExecution(
result: null,
stdout: '',
error: {
message: `Execution queue is full (${MAX_QUEUE_SIZE}). Please retry later.`,
name: 'QueueFullError',
message: 'Code execution is at capacity. Please try again in a moment.',
name: 'Error',
},
})
return
@@ -877,8 +878,9 @@ function enqueueExecution(
result: null,
stdout: '',
error: {
message: `Owner queue limit reached (${MAX_QUEUED_PER_OWNER}). Please retry later.`,
name: 'OwnerQueueLimitError',
message:
'Too many concurrent code executions. Please wait for some to complete before running more.',
name: 'Error',
},
})
return
@@ -892,8 +894,8 @@ function enqueueExecution(
result: null,
stdout: '',
error: {
message: `Execution queued too long (${QUEUE_TIMEOUT_MS}ms). All workers are busy.`,
name: 'QueueTimeoutError',
message: 'Code execution timed out waiting for an available worker. Please try again.',
name: 'Error',
},
})
}, QUEUE_TIMEOUT_MS)
@@ -973,8 +975,9 @@ export async function executeInIsolatedVM(
result: null,
stdout: '',
error: {
message: `Owner in-flight limit reached (${DISTRIBUTED_MAX_INFLIGHT_PER_OWNER}). Please retry later.`,
name: 'OwnerInFlightLimitError',
message:
'Too many concurrent code executions. Please wait for some to complete before running more.',
name: 'Error',
},
}
}
@@ -983,8 +986,8 @@ export async function executeInIsolatedVM(
result: null,
stdout: '',
error: {
message: 'Distributed fairness is temporarily unavailable. Please retry.',
name: 'DistributedFairnessUnavailableError',
message: 'Code execution is temporarily unavailable. Please try again in a moment.',
name: 'Error',
},
}
}