← All posts
5 min read

Finding the Real Bottleneck: Optimize the Constrained Path

#technology#performance#bottlenecks#systems-at-scale
📑 On this page

When a system feels slow, many components look improvable.

Web servers use CPU, queries scan rows, network calls take time, and queues contain work. Improving a component that is not limiting the outcome may produce no visible benefit.

A bottleneck is the resource or stage currently constraining end-to-end throughput or latency.

Measure the full path before deciding where to optimize.

A concrete example: adding web servers

Checkout latency is high, so a team doubles the number of web servers.

Nothing improves. Traces show every request waits on one database lock. Extra web servers create more concurrent waiters and increase connection pressure.

The database contention, not web compute, is the current bottleneck.

Throughput and latency

Throughput measures completed work per time.

Latency measures time for one operation.

The same constraint can affect both, but not always equally. A queue may preserve throughput while increasing waiting latency.

Define which outcome needs improvement.

End-to-end measurement

Break the user path into stages:

  • client,
  • network,
  • edge,
  • application,
  • dependency,
  • database,
  • queue,
  • rendering.

Measure time and waiting at each stage. Local microbenchmarks cannot reveal remote queueing.

Critical path

The critical path is the chain of work determining total completion time.

Parallel tasks do not simply add:

total ≠ task A + task B

If A and B run together, the slower branch may dominate before later work continues.

Traces reveal this shape.

Service demand

Estimate how much of each resource one unit of work consumes:

  • CPU milliseconds,
  • database queries,
  • disk reads,
  • network bytes,
  • lock duration.

Multiplying demand by arrival rate indicates which resource approaches capacity.

Saturation

A resource is saturated when demand approaches or exceeds what it can serve.

Signals include:

  • CPU run queue,
  • database connection wait,
  • disk queue,
  • thread-pool exhaustion,
  • memory pressure,
  • queue age,
  • and rate-limit rejection.

High utilization alone is not always harmful if latency remains stable.

Queueing

As utilization nears capacity, waiting can rise sharply.

Small demand variation creates large latency tails because new work waits behind existing work.

Measure queue time separately from execution time.

Workload matters

A bottleneck belongs to a workload:

  • read-heavy browsing,
  • write-heavy sale,
  • large report,
  • cache cold start,
  • or regional failover.

An optimization for one workload can hurt another. Reproduce representative traffic and data.

Averages hide tails

Average latency can remain acceptable while p99 becomes terrible.

Tail requests may share:

  • one large tenant,
  • cold cache,
  • rare query shape,
  • overloaded shard,
  • or distant region.

Segment metrics by meaningful dimensions.

Distributed tracing

Traces show:

  • span duration,
  • parent-child relationship,
  • parallel work,
  • retries,
  • and dependency calls.

Sampling should preserve slow and failed traces.

Trace duration still needs resource metrics to explain why a span was slow.

Profiling

CPU profiles show where execution time is spent inside a process.

Memory profiles identify allocation and retention.

Use profiling after evidence points to the process. Profiling an idle web server will not explain database lock contention.

Database analysis

Inspect:

  • slow-query fingerprints,
  • execution plans,
  • lock waits,
  • index use,
  • buffer hit rate,
  • connections,
  • and replication lag.

One query may be fast alone but destructive under concurrency.

Load tests

Increase load gradually while observing:

  • throughput,
  • latency,
  • errors,
  • and resources.

The saturation point shows where throughput stops growing and latency rises.

Test correctness as well as speed.

Controlled experiments

Change one meaningful factor:

  • index,
  • instance size,
  • cache policy,
  • concurrency,
  • or batch size.

Compare against baseline under the same workload.

Changing several factors at once hides which one mattered.

Coordinated omission

A load generator that waits for slow responses may send less traffic during failure.

It underreports requests real users would continue producing.

Use arrival-rate-aware tools where appropriate.

Removing one bottleneck

After improving the current constraint, another becomes limiting.

This is normal. Performance work is iterative:

  1. measure,
  2. identify,
  3. improve,
  4. verify,
  5. repeat.

There is no permanently bottleneck-free system.

Caching

Caching can remove repeated expensive work.

It can also shift the bottleneck to:

  • cache memory,
  • invalidation,
  • miss storms,
  • or origin refill.

Measure hit ratio and miss cost, not cache presence alone.

Batching

Batching amortizes fixed cost across several items.

It may increase waiting while the batch fills and create large failure units.

Choose batch size using throughput and deadline evidence.

Cost bottlenecks

The limiting constraint can be budget or quota rather than hardware.

Examples:

  • external API limit,
  • cloud cost ceiling,
  • licensing,
  • or rate-limited partner.

Architecture should optimize the scarce business resource.

Capacity models

A simple capacity model records for each important operation:

  • arrival rate,
  • service time,
  • concurrency,
  • resource demand,
  • and maximum useful wait.

The model does not need perfect prediction. It exposes assumptions, such as one checkout using four database queries, so measurements can show which assumption changed as traffic and features evolve.

Optimization guardrails

Performance changes must preserve:

  • correctness,
  • durability,
  • security,
  • and maintainability.

Removing a transaction or validation step can produce a faster benchmark while making the product unsafe. Define functional and resource guardrails before comparing results.

Knowledge check

  1. Why can adding web servers make database contention worse?
  2. What does the critical path determine?
  3. Why should queue time be measured separately?
  4. How do profiling and end-to-end tracing serve different purposes?
  5. Why does a new bottleneck appear after optimization?

The one idea to remember

Performance improves when engineers measure the complete workload path and change the resource or stage actually limiting the target. Use traces, resource saturation, queueing, profiles, database evidence, and controlled load tests before optimizing.