Skip to content

feat(transform-eval): per-org concurrency bound for vector subprocess (SC-2)#499

Merged
TerrifiedBug merged 1 commit into
mainfrom
feat/sc-2-per-org-concurrency
Jun 8, 2026
Merged

feat(transform-eval): per-org concurrency bound for vector subprocess (SC-2)#499
TerrifiedBug merged 1 commit into
mainfrom
feat/sc-2-per-org-concurrency

Conversation

@TerrifiedBug

Copy link
Copy Markdown
Owner

SC-2 — per-org fairness for the heavy vector subprocess

evaluateVrl() (src/server/services/transform-eval.ts) spawns a vector subprocess (execFile, 15s) on every VRL eval. There was no cross-request per-org bound, so one tenant issuing many concurrent evals (live-tap iteration, cost what-if, unit-test "run all", the AI propose auto-fix loop) could spawn unbounded vector processes and starve the shared host.

Change

  1. New src/lib/org-concurrency.tswithOrgConcurrencyLimit(orgId, key, max, fn): an in-process per-(org, key) async semaphore. Caps concurrent fn executions for the pair, queues the rest FIFO, hands a freed slot directly to the next waiter, and always releases on success/throw. A missing/empty orgId collapses to a shared "global" bucket; idle buckets are pruned so the Map tracks live concurrency, not historical org count. Single-process (per Node worker) — fairness within a host, not a distributed limiter.
  2. evaluateVrl(source, events, opts?) gains optional opts.orgId. When set, the subprocess spawn runs under withOrgConcurrencyLimit(orgId, "vector-eval", 4). The no-op fast path (empty program/input) never takes a slot, and behavior is identical when orgId is omitted (backward-compatible).
  3. Call sites threaded with { orgId: ctx/input.organizationId }:
    • vrl.tsrunUnitTests, runPipelineUnitTests (via runTestsAgainstSource), testAgainstCapture
    • tap-capture.tstestTransform (live-tap)
    • cost-recommendation-procedures.tssimulateTransform (cost what-if)
    • proposed-change.ts — VRL validate + bounded auto-fix loop

Tests

src/lib/__tests__/org-concurrency.test.ts uses controllable deferred barriers (no real timers): caps concurrency (max=2, 5 tasks → peak 2), drains FIFO in order, isolates different orgs and different keys, falls back empty/whitespace orgId → shared "global" bucket, and releases the slot on throw.

  • vitest run src/lib/__tests__/org-concurrency.test.ts src/server/services/__tests__/transform-eval.test.ts15 passed
  • Affected suites (tap-capture, vrl-unit-test, vrl-pipeline-unit-test, cost-recommendation-simulate, proposed-change) → 60 passed (updated the evaluateVrl arg assertions to the new 3-arg form)
  • tsc --noEmit filtered to changed files: clean — only the known worktree-local @clickhouse/client module-resolution error remains (unrelated to this change).

Notes

  • No Prisma migration.
  • Editor / agent / cloud-UI paths aren't locally or visually verifiable here; unit tests + types are the bar.

Add src/lib/org-concurrency.ts: an in-process per-(org,key) async semaphore
(withOrgConcurrencyLimit) that caps concurrent fn executions, queues the rest
FIFO, and always releases on success/throw. A missing/empty orgId falls back to
a shared "global" bucket; idle buckets are pruned.

Thread an optional orgId into evaluateVrl(): when provided, the vector
subprocess spawn runs under withOrgConcurrencyLimit(orgId, "vector-eval", 4) so
one tenant's concurrent VRL evals (live-tap, cost what-if, unit-test "run all",
AI propose auto-fix loop) can no longer spawn unbounded vector processes and
starve the shared host. The no-op fast path never takes a slot, and behavior is
identical when orgId is omitted (backward-compatible).

Pass ctx/input organizationId at the org-scoped call sites: vrl
runUnitTests/runPipelineUnitTests/testAgainstCapture, tap-capture testTransform,
cost-model simulateTransform, and proposed-change VRL validation.
@TerrifiedBug TerrifiedBug merged commit d46c5ba into main Jun 8, 2026
17 checks passed
@TerrifiedBug TerrifiedBug deleted the feat/sc-2-per-org-concurrency branch June 8, 2026 12:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant