One slow payment provider must not take down the checkout
10:00, flash sale. The payment provider answers in 3 s instead of 300 ms. Within a minute order stops answering everything — even GET /orders/{id}. Why?
order
GET /orders/{id}
payment
POST /payments
psp
POST /v1/charges
approved
toxiproxy
toxiproxy:18085
toxiproxy:18095
POST /orders/{id}/pay
CONFIRMED
OrderConfirmed
latency
jitter
timeout
reset_peer
bandwidth
enabled: false
curl -X POST localhost:8474/proxies/payment/toxics \ -d '{"name":"slow","type":"latency","attributes":{"latency":3000}}'
toxicity 0–1: the probability per connection. POST /reset is the abort button.
toxicity
POST /reset
factory.setConnectTimeout(Duration.ofMillis(500)); factory.setReadTimeout(Duration.ofSeconds(1)); // 0 = forever (the default!)
Budgets shrink at each hop: edge 10 s > gateway 5 s > order→payment 1 s × attempts.
max-attempts: 3 wait-duration: 200ms enable-exponential-backoff: true # 200 ms, 400 ms enable-randomized-wait: true randomized-wait-factor: 0.5 # ± 50% jitter retry-exceptions: [ResourceAccessException, HttpServerErrorException]
Idempotency-Key: order-<id>
UNIQUE
200
A timeout does not mean the call failed — the response may have been lost. The key makes the retry harmless.
Watch it: /actuator/circuitbreakers, /actuator/circuitbreakerevents.
/actuator/circuitbreakers
/actuator/circuitbreakerevents
Calls succeed in 700 ms — below the timeout and the slow-call threshold. At 70/s, GET /orders/{id} slows down anyway. Why, and what fixes it?
max-concurrent-calls: 10
max-wait-duration: 0
503
@Retry(name = "payment", fallbackMethod = "unavailable") @CircuitBreaker(name = "payment") @Bulkhead(name = "payment") public PaymentResponse charge(UUID orderId, BigDecimal amount) { ... }
CallNotPermittedException
BulkheadFullException
pay()
UPDATE … WHERE status = 'PENDING_PAYMENT'
pay
browse
pay_fast_fail
k6 load/pay-under-chaos.js: pay (uses payment) and browse (does not) at the same time — the second one reveals the cascade.
load/pay-under-chaos.js
No hypothesis, no experiment. A refuted hypothesis is the valuable result.
cascading-failure
retry-storm
exp+jitter
The simulator shows the shape; k6 + Toxiproxy give you your own numbers.
Outline: (1) the cascade; (2) payment, psp, Toxiproxy; (3) timeout; (4) retry + idempotency; (5) circuit breaker; (6) bulkhead; (7) game day; (8) measure.