CONCEPT Cited by 1 source
Non-idempotent endpoint parallel-run constraint¶
Definition¶
The non-idempotent endpoint parallel-run constraint is the applicability limit of the parallel run pattern: endpoints with side effects that cannot be double-executed safely (POSTs that write to a database, publish events, call mutating downstream APIs, charge a card, send an email) cannot be put under parallel run without causing real-world duplication. Read-only endpoints and idempotent POST/PUT/PATCH operations are safe; truly non-idempotent endpoints are not.
Mechanism of the problem¶
Under parallel run:
- Request hits monolith → monolith performs the side effect (e.g., inserts a refund row, sends a Kafka event).
- Monolith fires async comparison → new service re-issues the request against its own real endpoint.
- New service also performs the side effect → duplicate refund row, duplicate Kafka event, duplicate email.
If downstream systems rely on the operation being executed exactly
once, parallel run breaks that invariant silently. The duplication
may not even be visible in the comparison metric (both systems
return 200 OK with identical bodies) — the damage is out-of-band.
Zalando framing¶
"Idempotency should always be kept into account. For example this approach can be used for POSTs that are idempotent but not when the idempotency of the endpoint cannot be guaranteed. When doing this investigation always consider idempotency of each operation and possible side effects (for example calling another POST api, updating a database, or publishing an event)." (Source: sources/2021-11-03-zalando-parallel-run-pattern-a-migration-technique-in-microservices)
Zalando names the constraint but does not provide a resolution pattern.
What counts as idempotent¶
- Read-only (GET, HEAD) — always safe.
- Idempotent mutations — PUT with the full target state (setting a row to a specific value twice is the same as once); DELETE-by-id (deleting a row that's already deleted is a no-op).
- POST with idempotency key — if the endpoint deduplicates on a client-supplied key, re-invocation is effectively a read.
- Side-effect-free POSTs — e.g., search POSTs that return computed results without persisting anything.
What doesn't¶
- POST that creates a row without an idempotency key — duplicate rows.
- POST that publishes an event without deduplication — duplicate events downstream.
- POST that calls a mutating downstream API without idempotency guarantees — duplication propagates.
- POST that charges money, sends an email, triggers a workflow — real-world duplication, often unrecoverable.
Mitigations (not in Zalando's post, inferred)¶
- Stub the mutating dependency in the new service during parallel run. The new service runs the logic but doesn't actually write to the DB / publish the event. Compare the intended side effect (what the new service would have written) against the monolith's actual side effect. This is a shape change — more like shadow mode than parallel run.
- Use idempotency keys. Forward the monolith's idempotency key through to the new service so the second invocation deduplicates.
- Skip parallel run for non-idempotent endpoints. Cover them with other patterns (integration tests, staged rollout with feature flags, shadow production load tests).
- Asymmetric parallel run. Only run the side-effect-free portion of the request in the new service (e.g., validation, computed fields) and compare that — don't re-execute the mutation.
Seen in¶
- Zalando Returns-service extraction (2021-11-03, sources/2021-11-03-zalando-parallel-run-pattern-a-migration-technique-in-microservices) — constraint flagged as a "consideration and limitation".