Konrad Kowalski (rootsher)Principal Platform & Reliability Architect011010011011101110001110000000000010000011000011

Edge rendering: compute moved closer to the user

date
category
Frontend
also in
Cloud · System Design
reading
4 min / 765 words

So far we have been moving rendering mostly in time.

It could happen:

text
during the build
on the first request
on every request
or in fragments through streaming

But there is a second axis:

where is the code physically executed?

If the user is in Warsaw, the application runs in us-east-1 and the database sits in Virginia, every dynamic request carries a geographic cost.

The natural idea is:

move the compute closer to the user.

The origin does not have to be the only place of execution

The classic model:

text
browser
  |
  v
CDN
  |
  v
origin
  |
  v
database

A CDN handles static assets beautifully.

But dynamic HTML still often requires a round trip to the origin.

Edge rendering tries to move part of the execution:

text
browser
  |
  v
edge runtime
  |
  v
response

If the user is a few milliseconds from an edge node, the first part of the request can be much shorter.

"Closer to the user" sounds better than it is

Assume:

text
user -> edge      10 ms
user -> origin   120 ms

At first glance the edge wins hands down.

But a render usually needs data.

If the database still sits next to the origin:

text
user
  | 10 ms
  v
edge
  | 110 ms
  v
database
  | 110 ms
  v
edge
  | 10 ms
  v
user

then we moved compute closer to the user but further from the data.

In practice the most important question is not:

where is the renderer?

but:

where is the whole dependency graph needed for the response?

A commercial example: a global storefront

Imagine a store operating in Europe, the US and Asia.

Part of the page is shared:

text
layout
product description
photos
marketing content

Part depends on the region:

text
currency
taxes
promotions
availability
language

An edge runtime can be a very good fit for lightweight logic:

text
request
  |
  v
detect region
  |
  v
select locale
  |
  v
read edge cache
  |
  v
compose response

If the needed data is already available locally, you can avoid an expensive round trip to a central origin.

That is why the edge combines well with:

text
CDN cache
KV storage
geolocation
routing
simple personalization

An edge runtime is often a different runtime

In practice "edge" does not always mean simply:

text
Node.js, only closer to the user

The code often runs in a more constrained environment.

For example, in an isolate-based model:

text
request
  |
  v
existing lightweight isolate
  |
  v
execute handler

instead of:

text
request
  |
  v
start process/container
  |
  v
initialize runtime
  |
  v
execute handler

That can reduce startup overhead, but usually limits the available APIs and the way native dependencies can be used.

A framework may hide much of that difference, but the runtime is still a different one.

A curiosity

A V8 isolate is not a separate process.

It is an isolated JavaScript execution context inside a V8 process, with its own heap and runtime state.

Thanks to that many isolates can share one process instead of starting a separate system process for every request.

That gives a very good startup and density profile.

But an isolate boundary does not automatically provide the same isolation as a process boundary.

That is one of the reasons edge runtimes come with their own set of constraints and sandboxing mechanisms.

Data locality beats marketing

Assume two variants.

Origin rendering

text
user -> origin: 100 ms
origin -> database: 2 ms
render: 15 ms

Edge rendering

text
user -> edge: 10 ms
edge -> database: 90 ms
render: 15 ms

The edge gives no spectacular advantage here.

If the render runs several sequential queries:

text
edge
  |
  v
DB query A
  |
  v
DB query B
  |
  v
DB query C

the problem may be even worse.

Every database connection adds another RTT.

That is why edge architecture very often forces a change not only in where the frontend runs but also in how it accesses data.

Data replication changes the game

If we can replicate the data:

text
US database
EU replica
Asia replica

then compute and data can sit close to each other.

But a new problem appears immediately:

consistency.

If a user changes their delivery address in Europe, when will the update be visible in the Tokyo replica?

For marketing content a delay of a few seconds may be irrelevant.

For account state or payments it is not.

So the edge pushes the frontend into the world of classic distributed systems problems:

text
latency
consistency
replication
data ownership

The edge is great for a certain class of work

Not everything has to move.

Good candidates are usually operations that are:

text
short
request-specific
location-dependent
highly cacheable
free of many distant queries

For example:

text
redirect
locale detection
A/B assignment
authentication check
cache lookup
response composition

A much harder case:

text
complex ORM query
+
multiple internal APIs
+
transaction
+
large server render

Deploying "to the edge" does not by itself fix a dependency architecture.

Edge and SSR are different decisions

This is an important distinction.

SSR answers:

when is the HTML produced?

Edge answers:

where is the code executed?

We can have:

text
SSR on origin
SSR on edge
static HTML on edge CDN
client rendering with edge API

Those are independent axes.

Modern frameworks often present them together, which makes it easy to start treating "edge rendering" as a separate type of rendering.

Architecturally it is above all a decision about compute location.

And again we come back to the client

So far we have been trying to improve the first delivery of HTML.

We moved rendering:

text
build
request
stream
edge

But the second half of the problem remains.

If we ship the user a large JavaScript application, the browser may still perform a full hydration no matter how fast the HTML was produced.

So we can optimise the server to its limits and then hand the main thread an enormous component tree to activate.

The natural question is:

does the whole page really have to become interactive right away?

That leads to the next class of solutions: selective and progressive hydration.