Cloudflare Workers Limits Audit

Sources reviewed:

  • relay/cf/src/workspace-doc.ts — Durable Object broker
  • api/src/routers/*.ts — REST handlers (Hono workers; workspaces.ts, misc.ts, llm.ts, telemetry.ts, p2p.ts, internal.ts)
  • mcp/src/index.ts + realtime.ts + tools/*.ts — MCP worker

CF limits from: Cloudflare Workers documentation (paid / Workers Paid plan assumed).


Limit-by-Limit Assessment

1. CPU Time per Request

Limit: 50 ms (free) / 30 s (paid) per invocation.

Handler / path Work done Risk
WebSocket webSocketMessage Decodes frame → validateUpdate (Zod + Y.Doc clone) + Y.applyUpdate + SQLite append MediumvalidateUpdate clones the Y.Doc (proportional to doc size). At 10k jobs that clone alone is ~20 ms.
applyOpRequest (DO REST, used by MCP) Two Y.Doc clones (probe + canonical) + Zod validation Medium — same clone cost, times two.
alarm()maybeCompact + D1 backup Y.encodeStateAsUpdate + SQLite write + D1 UPDATE Low/Medium — I/O-bound; CPU portion is the encode step. At 10k jobs encode is ~5 ms.
GET /api/workspaces/:id D1 SELECT + JSON.parse + maskCloudKeys Low — O(1) I/O, trivial CPU.
POST /api/workspaces D1 INSERT x2 + migrateProfileMap (one-off) Low — migration runs once per workspace.
GET/POST /api/workspaces/:id/jobs D1 SELECT/INSERT + optional DO service-binding call Low — all I/O.
POST /api/proxy Single outbound fetch with 30 s timeout Low CPU, High wall time — the 30 s timeout is fine on paid but blocks the thread.
MCP tools/callapplyOp service binding Zod schema parse + service-binding fetch to DO Low — CPU work is tiny; latency dominated by the service-binding round-trip.

Verdict: The validateUpdate / Y.Doc clone path is the only realistic CPU hot spot. It is proportional to Y.Doc size. Under the paid 30 s budget this is not a hard limit today, but at > 5 000 jobs it will consume noticeable CPU.


2. Subrequest Count

Limit: 50 (free) / 1 000 (paid) per request.

Handler Subrequests Notes
webSocketMessage 1 (SQLite write, counted as storage I/O, not subrequest) No external fetches.
POST /api/workspaces/:id/jobs 1 D1 SELECT + 1 D1 INSERT + 1 DO service-binding 3 total.
POST /api/proxy 1 outbound fetch to allow-listed domain 1 total.
MCP tools/call (mutating) 1 D1 query + 1 REALTIME service binding + 1 DO /ops/ internal 3 total.
MCP tailor tool D1 read + DO snapshot read (GET /ops/:wsId?keys=...) + D1 write ~3 total.

Verdict: No handler comes close to the 50-subrequest free-tier limit, let alone 1 000. No risk.


3. Memory per Isolate

Limit: 128 MB per Worker isolate (shared across all requests in the same isolate).

Component Memory usage Notes
WorkspaceDoc DO — Y.Doc at 1k jobs ~1.9 MB raw Safe.
WorkspaceDoc DO — Y.Doc at 10k jobs ~18.4 MB raw Approaches 14% of limit — manageable but start watching.
WorkspaceDoc DO — Y.Doc probe clone (applyOpRequest) Same as doc size Doubles peak usage during op validation.
Web worker isolate Minimal — no in-memory Y.Doc Only JSON parsing.
MCP worker isolate Minimal — no Y.Doc Only JSON-RPC routing.

Verdict: Medium risk at > 8 000 jobs per workspace. A Y.Doc for a 10k-job workspace is ~18 MB; the probe clone doubles that to ~37 MB during applyOpRequest. At 65k jobs the doc would hit 128 MB in isolation, leaving no room for the clone. Evicting tailored_cv/tailored_cl from terminal jobs (see YJS-SIZE-BENCH.md) reduces per-job overhead by ~70% and pushes the effective limit to ~200k jobs.


4. DO Storage Budget

Limit: ~5 GB per Durable Object on the paid plan (SQLite-backed).

Using the compressed figure of ~0.13 KB/job:

Jobs DO storage (compressed)
10 000 1.3 MB
100 000 12.6 MB
1 M 126 MB

Even at 1 million jobs the DO storage cost is 126 MB — well under 5 GB.

The ops log (unapplied Yjs deltas between snapshots) adds headroom proportional to the compaction threshold (200 ops). Each op is a small delta; even at 200 ops the log is < 1 MB.

Verdict: Low risk. DO storage is not a binding constraint for any realistic workspace size.


5. WebSocket Message Size

Limit: 1 MB per WebSocket message.

Message type Max size Notes
Sync step 2 (server → client) Bounded by CATCHUP_CAP_BYTES = 2 MB Exceeds the 1 MB WS limit for lagged clients. The cap is applied before sending, but the cap itself is 2 MB — double the WS limit.
broadcastUpdate Size of the incoming Yjs op delta Deltas are typically < 1 KB for single-field edits. Tailored CV writes could be 1–2 KB.
Awareness update Small (clientID + cursor position) < 1 KB.

Verdict: Low/Medium risk. Individual op deltas and awareness frames are well under 1 MB. The catch-up path (CATCHUP_CAP_BYTES = 2 MB) exceeds the WS message limit and would silently fail for a severely lagged client. The constant should be lowered to 900 KB or the payload should be split across multiple frames.


6. Concurrent WebSocket Connections per DO

Limit: 32 000 theoretical; practically constrained by memory (each session entry + awareness state consumes ~1–2 KB).

CareerVector is a per-workspace DO. A workspace is accessed by 1–20 users in typical usage. The theoretical limit is irrelevant here. Even if a workspace became viral (enterprise team of 1 000 users), 1 000 concurrent WebSocket connections would consume ~2 MB of session overhead — trivial.

Verdict: No risk.


7. DO Alarm Precision

Limit: Alarms fire within ~30 s of the scheduled time; not real-time.

The D1 backup alarm is set hourly. The compaction alarm fires within the 5-minute threshold. Neither is latency-sensitive.

Verdict: No risk.


Risk Matrix

Limit Risk Trigger condition
CPU time per request Medium Y.Doc > 5k jobs, validateUpdate clone > 20 ms
Subrequest count Low Max 3 subrequests per handler today
DO memory (128 MB) Medium Y.Doc raw size > 64 MB (~33k jobs without eviction)
DO storage (5 GB) Low Would need 40M+ jobs at current compression ratio
WebSocket message size (1 MB) Low Catch-up cap set at 2 MB — exceeds limit for lagged clients
Concurrent WS connections Low Per-workspace; 1–1000 users, far below 32k
Alarm precision Low Hourly backups, not latency-sensitive

Top 3 Most-Likely-to-Hit Limits

#1 — DO Memory (Medium risk, ~33k jobs without mitigation)

The raw Y.Doc is loaded fully into memory on every DO wake. At 1.89 KB/job, a 33k-job workspace hits the 64 MB ceiling (half of 128 MB, leaving room for the probe clone). With tailored_cv eviction this extends to ~80–100k jobs.

Mitigation:

  • Evict tailored_cv / tailored_cl from terminal jobs (see YJS-SIZE-BENCH.md §1).
  • If per-workspace job counts routinely exceed 10k, split the Y.Doc: scalar job fields in the main doc, tailored_cv/tailored_cl in a secondary per-job doc or R2 blob, fetched on-demand.

#2 — CPU Time (Medium risk, > 5k jobs under concurrent writes)

validateUpdate clones the full Y.Doc before applying each op. The clone is O(doc size). Under a burst of concurrent writes from multiple clients (e.g., an LLM pipeline that fires 20 ops in parallel), the CPU budget can be consumed quickly at large workspace sizes.

Mitigation:

  • Apply tailored_cv eviction (reduces clone cost ~70%).
  • Consider a validation mode that skips the full clone for known-safe op kinds (e.g., job.update with only scalar fields), falling back to full-clone validation only for structural changes.

#3 — WebSocket Message Size (Low/Medium risk, catch-up path)

CATCHUP_CAP_BYTES = 2_000_000 (2 MB) in workspace-doc.ts exceeds the 1 MB WS message size limit. A severely lagged client (not connected for > 5 minutes, with heavy concurrent writes) could receive a catch-up payload that silently truncates.

Mitigation (one-line fix):

// workspace-doc.ts line 45
const CATCHUP_CAP_BYTES = 900_000; // Stay under 1 MB WS limit

Or: split the catch-up into multiple sync frames (more complex but avoids the silent truncation).


Non-Issues Worth Noting

  • Subrequest chaining: The MCP worker sends one service-binding call to REALTIME, which makes one DO fetch. That is 2 subrequests total — nowhere near the 1 000 paid limit.
  • DO storage: Extremely cheap at this scale. Not a factor.
  • Workers script size: The hand-rolled JSON-RPC framing in the MCP worker (mcp/src/index.ts) is intentionally thin (~180 LoC). No bundle size risk.
Source: wiki/content/architecture/CF-WORKERS-LIMITS.md