Bulkheads: one slow dependency should not take the pool

Fifty workers all waiting on the same upstream is a site that is down because of a service most of its pages do not use.

pm.max_children = 50

without bulkheads:
  one slow dependency at 20s → the whole pool at 2.5 req/s

with a concurrency cap per dependency:
  payments:  max 10 concurrent   → 40 workers still free
  search:    max 10 concurrent
  analytics: max 5  concurrent

the cap is the bulkhead. the timeout is not.

A timeout bounds how long one request waits and says nothing about how many wait simultaneously, which is the arithmetic people miss: workers divided by upstream latency is the maximum request rate. A semaphore in Redis with an expiry is a workable cap in PHP and is cruder than the thread pools other platforms use for this. Rejecting immediately when the cap is reached is the point — a queue behind the bulkhead is the pool again with extra steps.