Summary
When firing multiple stub.rpc() calls on a Durable Object stub without awaiting each one, adjacent calls occasionally swap order in vitest. This does not happen in production, wrangler dev, or vite dev — only in @cloudflare/vitest-pool-workers.
The reordering is non-deterministic: different runs show swaps at different positions, and small N (like 20) sometimes passes. The effect is more pronounced at higher N.
Reproduction
Repo: https://github.com/threepointone/test-do-rpc-fetch-ordering
The "rpc calls preserve send order" test will intermittently fail, and "rpc with N=100" fails consistently. Example output:
expected: [..., "call-5", "call-6", ...]
actual: [..., "call-6", "call-5", ...]
With N=100, multiple swap pairs are scattered throughout the sequence.
For comparison, the same test logic passes reliably when run via wrangler dev or in production:
# wrangler dev — always passes
npx wrangler dev
curl http://localhost:8787/test-rpc-ordering?n=100
# production — always passes
curl https://test-ordering.threepointone.workers.dev/test-rpc-ordering?n=100
Likely Cause
The vitest pool communicates with workerd over a WebSocket transport. The dispatching of individual RPC calls through this transport likely introduces a race condition where adjacent calls can be reordered before they reach workerd's event loop. workerd's InputGate correctly processes them in arrival order, but they arrive in the wrong order.
This is supported by the fact that:
- The swaps are always between adjacent calls (not large jumps)
- The issue is non-deterministic (consistent with a race, not a logic bug)
- Production and wrangler dev (which don't use the WebSocket transport) never exhibit this
Related
There is a separate, more severe ordering bug in workerd itself where mixed stub.rpc() + stub.fetch() calls are always reordered. That is tracked at cloudflare/workerd#6561 and is unrelated to this vitest-specific issue.
Summary
When firing multiple
stub.rpc()calls on a Durable Object stub without awaiting each one, adjacent calls occasionally swap order in vitest. This does not happen in production,wrangler dev, orvite dev— only in@cloudflare/vitest-pool-workers.The reordering is non-deterministic: different runs show swaps at different positions, and small N (like 20) sometimes passes. The effect is more pronounced at higher N.
Reproduction
Repo: https://github.com/threepointone/test-do-rpc-fetch-ordering
npm install npm testThe "rpc calls preserve send order" test will intermittently fail, and "rpc with N=100" fails consistently. Example output:
With N=100, multiple swap pairs are scattered throughout the sequence.
For comparison, the same test logic passes reliably when run via
wrangler devor in production:Likely Cause
The vitest pool communicates with workerd over a WebSocket transport. The dispatching of individual RPC calls through this transport likely introduces a race condition where adjacent calls can be reordered before they reach workerd's event loop. workerd's
InputGatecorrectly processes them in arrival order, but they arrive in the wrong order.This is supported by the fact that:
Related
There is a separate, more severe ordering bug in workerd itself where mixed
stub.rpc()+stub.fetch()calls are always reordered. That is tracked at cloudflare/workerd#6561 and is unrelated to this vitest-specific issue.