EnginePreOriginHandler (OnOriginRequest) to adapt to a breaking behavioral change in how request deduplication works.
What Changed
In older versions of the Cosmo Router, request deduplication (singleflight) happened at the HTTP transport layer. TheCustomTransport.RoundTrip method would hash the request body and all headers to compute a deduplication key. Since EnginePreOriginHandler.OnOriginRequest hooks ran before the transport layer, any headers set in OnOriginRequest were naturally included in the deduplication key. Two requests with different custom headers would correctly be treated as distinct requests.
Starting with Router 0.278.0, request deduplication has moved to the engine/loader layer. The router now pre-computes subgraph header hashes based on configured header forwarding rules before calling into the engine — which is after all middleware runs but before any OnOriginRequest hook fires. See Request Deduplication for details on how the new deduplication works.
This means:
- Headers set in
OnOriginRequestare not included in the deduplication key. - Two concurrent requests that differ only by a header set in
OnOriginRequestmay be incorrectly deduplicated — one request’s response will be served to both clients. - As a safety measure, the router automatically disables both levels of request deduplication whenever any
EnginePreOriginHandleris registered. This prevents incorrect behavior but sacrifices a significant performance optimization.
What You Need to Do
Scenario 1: You set headers in OnOriginRequest that affect request identity
Example: setting a tenant ID, user-specific token, or any header whose value varies between requests and should prevent deduplication.
Old code (broken with new dedup):
RouterOnRequest or Middleware
config.yaml, add a rule that tells the router to forward this header to subgraphs:
- The header value is read from the inbound request during hash computation
- Different values produce different hashes, preventing incorrect deduplication
- The header is forwarded to the subgraph automatically — no
OnOriginRequestneeded - Request deduplication remains enabled (no performance loss)
EnginePreOriginHandler implementation
If this was the only reason for your OnOriginRequest hook, remove it entirely. Remove the interface guard too:
EnginePreOriginHandler registered, the router will no longer auto-disable deduplication.
Scenario 2: You set headers in OnOriginRequest for signing or decoration (not affecting identity)
Example: adding a request signature, timestamp, or trace correlation header that should not affect deduplication.
OnOriginRequest is the right place for this because:
- The signature should be unique per actual outgoing request, not per logical dedup group
- You do not want the signature to affect deduplication
OnOriginRequest hook does not set headers that should differentiate requests for deduplication purposes.
Scenario 3: You use OnOriginRequest to short-circuit with a mock response
Scenario 4: You use OnOriginRequest to inspect or log (read-only)
EnginePostOriginHandler.OnOriginResponse or using the force-enable flags.
Quick Reference: Which Hook for What
| Use Case | Recommended Hook | Why |
|---|---|---|
| Set headers that vary per user/tenant | RouterOnRequest or Middleware + header forwarding rule | Headers are included in dedup key |
| Read auth claims to set headers | Middleware (auth is done) + header forwarding rule | Has access to ctx.Authentication() |
| Add request signatures | OnOriginRequest + force-enable dedup | Signatures should be per actual request |
| Short-circuit with mock response | OnOriginRequest | Only place where you can return a custom response |
| Log/observe subgraph requests | OnOriginRequest or OnOriginResponse + force-enable dedup | Read-only, no dedup impact |
| Inspect subgraph responses | OnOriginResponse | Runs after the subgraph response is received |
| Block requests based on operation | Middleware | Has full operation context |
| Manipulate headers before auth | RouterOnRequest | Runs before auth middleware |
Common Pitfalls
-
Setting headers in
OnOriginRequestwithout a forwarding rule: The header reaches the subgraph but is invisible to deduplication. Two requests that should be distinct (different header values) may be collapsed into one. -
Forgetting to add the forwarding rule after moving header logic to middleware: If you set
X-Tenant-IDinRouterOnRequestbut don’t configure apropagaterule for it inheaders.all.request, the header will be on the inbound request but won’t be forwarded to subgraphs and won’t affect dedup. -
Using
force_enable_single_flightwhen your hook DOES set identity-affecting headers: This will cause incorrect deduplication — clients with different tenants/users may receive each other’s data. Only use force flags when the hook is purely decorative (signatures, logging). -
Not removing the
EnginePreOriginHandlerinterface after migration: Even if the method body is empty, having the interface implemented will register the module as a pre-origin handler and disable dedup.