NashTech Blog

Networking: The Hidden Cost of a Network Call – What Microservices Often Ignore

Table of Contents

In many architecture discussions, you’ll hear something like:

“Let’s split this into microservices. It’ll be more scalable.”

So the monolith becomes:

  • Auth service
  • User service
  • Order service
  • Payment service
  • Notification service

Everything looks clean and modular.

But then, a few months later:

  • Latency increases
  • CPU usage spikes
  • Retry storms appear
  • The system becomes fragile

What changed?

Nothing… except the number of network calls.


The dangerous assumption

Many engineers unconsciously assume:

A network call is almost free.

It looks simple in code:

var user = await userService.GetUser(id);

One line of code.

But under the hood, this single call may involve:

  • Kernel context switches
  • DNS lookup
  • TCP handshake
  • TLS handshake
  • Packet retransmissions
  • Congestion control

What looks like one function call is actually a complex multi-step protocol dance.


Local call vs network call

Local function call

CPU register → memory → function → return
Time: nanoseconds to microseconds

Network call

App Kernel NIC Switch Router Internet
Remote kernel Remote app
Response back the same way
Time: milliseconds to hundreds of milliseconds

Difference:

  • Local call: ~0.1–1 µs
  • Network call: 1–100+ ms

That’s 1,000 to 100,000 times slower.


What actually happens during one network call

Let’s say:

Service A → calls → Service B

Here’s what may happen internally.


Step 1: Kernel context switch

Your app runs in user space.

When it sends data:

  1. App makes a system call.
  2. CPU switches from:
    • User mode
    • To kernel mode
  3. Kernel handles the network stack.

Diagram:

User space (your app)
|
| system call
v
Kernel space (network stack)
|
v
Network card

Each context switch:

  • Costs CPU cycles
  • Flushes CPU caches
  • Adds latency

And it happens:

  • On send
  • On receive
  • Often multiple times per request

Step 2: DNS lookup (if not cached)

Before connecting to a service:

user-service.internal

The system must resolve it to an IP:

10.2.4.15

Flow:

App
|
v
OS resolver
|
v
DNS server
|
v
IP returned

Typical latency:

  • Cached DNS: ~0.1–1 ms
  • Uncached DNS: 5–50+ ms

In microservices with short-lived connections, DNS cost can become significant.


Step 3: TCP handshake

Before sending data, TCP must establish a connection.

The three-way handshake:

Client Server
| ------ SYN ------> |
| <--- SYN-ACK ----- |
| ------ ACK ------> |

This requires:

  • At least 1 round-trip time (RTT)

If RTT = 20 ms:

  • TCP handshake ≈ 20 ms

Step 4: TLS handshake (for HTTPS/gRPC)

If the connection is encrypted:

Client Server
| --- ClientHello --> |
| <-- ServerHello --- |
| <--- Certificate ---|
| --- Key exchange -->|

This may cost:

  • 1–2 extra RTTs

So:

If RTT = 20 ms:

  • TCP handshake: ~20 ms
  • TLS handshake: ~20–40 ms
  • Total before first byte: 40–60 ms

And this is before your API even starts processing.


Step 5: Actual data transfer

Now the request is finally sent.

But TCP doesn’t just blast data at full speed.

It uses congestion control.


Congestion control: why TCP starts slow

TCP uses a mechanism called slow start.

At the beginning:

  • It sends a small amount of data.
  • Waits for acknowledgments.
  • Gradually increases the sending rate.

Diagram:

Time
Packets sent:
1 2 4 8 16 ...

This prevents:

  • Network congestion
  • Packet loss
  • Router overload

But it also means:

Small, frequent requests never reach full speed.

This is exactly what happens in microservice architectures.


What happens when retries are added

Now add this common pattern:

If request fails → retry 3 times

In a microservice chain:

API Gateway
|
v
Service A
|
v
Service B
|
v
Service C

If each service retries 3 times:

Service A → 3 attempts
Service B → 3 attempts
Service C → 3 attempts

Worst-case amplification:

3 × 3 × 3 = 27 calls

One failing request becomes 27 network calls.

This is called a retry storm.

And it causes:

  • CPU spikes
  • Network congestion
  • Cascading failures

End-to-end latency of a single call

Let’s estimate a simple HTTPS call.

Assume:

  • RTT: 20 ms
  • DNS: 5 ms
  • App processing: 10 ms

Breakdown:

DNS lookup: 5 ms
TCP handshake: 20 ms
TLS handshake: 40 ms
Request/response: 20 ms
App processing: 10 ms
-------------------------
Total: 95 ms

Almost 100 ms for one call.

Now imagine:

API → Service A → Service B → Service C

Four sequential calls:

100 ms × 4 = 400 ms

And that’s under ideal conditions.


Why this matters in microservice architecture

In a monolith:

Function A Function B Function C

All in memory.

Latency:

  • Microseconds

In microservices:

Service A network Service B
network Service C

Latency:

  • Hundreds of milliseconds

So microservices introduce:

  • More network calls
  • More handshakes
  • More context switches
  • More congestion risks

A simple mental model

Monolith

[App]
| function call
v
[Module]

Microservices

[Service A]
|
| DNS
| TCP
| TLS
| kernel switches
v
[Service B]

One arrow = many hidden costs.


Practical lessons for DevOps and architects

1. Don’t over-split services

Not every module needs to be a microservice.

If two components:

  • Always call each other
  • Share the same lifecycle
  • Have tight coupling

They may belong in the same service.


2. Reuse connections

Avoid:

  • Opening a new TCP/TLS connection per request

Use:

  • Connection pooling
  • HTTP/2 or gRPC

This removes:

  • Handshake cost
  • DNS overhead

3. Be careful with retries

Retries are useful, but dangerous.

Use:

  • Exponential backoff
  • Jitter
  • Circuit breakers

Avoid:

  • Immediate aggressive retries

4. Measure network latency, not just CPU

Track:

  • p95 and p99 latency
  • Network RTT
  • Connection counts
  • Retry rates

The key takeaway

A network call is not just:

serviceB.DoSomething();

It is:

DNS → TCP → TLS → kernel switches → congestion control → retries

Microservices turn:

  • Cheap function calls
  • Into expensive network operations

Understanding these costs helps you:

  • Design better architectures
  • Avoid latency explosions
  • Prevent cascading failures
Picture of Duong Dao Viet

Duong Dao Viet

Leave a Comment

Suggested Article

Discover more from NashTech Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading