Why splitting a service can lower availability
Latency adds and availability multiplies - the maths that decides your architecture.
Open this lesson in the learning hubKey points
- In a synchronous call chain, availability multiplies. Five services at 99.9% each give 0.999 to the fifth power - about 99.5%, or roughly four hours of downtime a month instead of forty minutes.
- Latency adds in the same way, and worse: a request that fans out to ten services waits for the slowest, not the average. If each has a p99 of 200ms, the combined p99 is far above 200ms.
- This is why a monolith split naively into services is often less reliable than what it replaced. Nothing got worse individually - the composition did.
- The fix is not making each service more reliable, which has sharply diminishing returns. It is removing hard dependencies from the request path.
- Three ways to do that: cache the dependency so a failure serves stale data, default it so a failure serves something reasonable, or make it asynchronous so the request does not wait at all.
- Classify every dependency as critical or optional before you build. If checkout genuinely cannot proceed without the recommendations service, that is a design decision - and usually the wrong one.
Example
/*
* The arithmetic, made concrete.
*
* SEQUENTIAL CHAIN - availability multiplies:
*
* 1 service 99.9% -> 43 min/month down
* 3 services 0.999^3 = 99.70% -> 2.2 hours/month
* 5 services 0.999^5 = 99.50% -> 3.6 hours/month
* 10 services 0.999^10 = 99.00% -> 7.3 hours/month
*
* Every one of those services met its SLO. The composition did not.
*
* PARALLEL FAN-OUT - latency tracks the slowest:
*
* 10 calls, each p99 = 200ms
* P(all under 200ms) = 0.99^10 = 90.4%
* -> ~10% of requests are slower than ANY single call ever is
*
* This is why tail latency dominates a fan-out architecture.
*/
// Removing a hard dependency. The call still happens - the REQUEST no
// longer depends on it succeeding.
@Service
public class ProductPageService {
public ProductPage build(String sku) {
Product product = catalog.find(sku); // critical - must succeed
// Optional: a failure degrades the page, it does not fail the request.
List<Product> suggestions = recommendations.forSku(sku)
.onErrorReturn(List.of()) // empty section, page renders
.timeout(Duration.ofMillis(120)) // and it never waits long
.block();
return new ProductPage(product, suggestions);
}
}
/*
* With recommendations optional, the page availability is the catalog
* availability alone - 99.9% instead of 99.8%, and no tail latency added.
*/
Availability multiplies down a call chain, so the lever is removing hard dependencies - not making each service marginally better.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Microservices course, and every lesson in it is listed on the Microservices contents page.