---
title: 'Content Delivery Network'
source: 'https://academia.sh/en/courses/compute-and-networking-in-the-cloud/content-delivery-network'
course: 'Compute and Networking in the Cloud'
language: en
updated: '2026-09-13T05:07:38+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Calculates at which hit rate the total bill turns over when edge egress and origin egress are priced separately'
    - 'Separates with numbers that origin load dropping and the bill dropping are two different thresholds'
    - 'Compares the three invalidation methods by requests, fee, window, dropped objects, and warming requests'
    - 'Counts item by item how many of the cache key, freshness, compression, and distribution decisions stay with the renter'
---

# Content Delivery Network

Measuring the edge rented as a service: the bill turning over at a 67% hit rate when edge egress and origin egress are priced separately, and the bill still staying high at 60% even though origin load has dropped; prefix invalidation looking free while producing 180,000 warming requests; invalidation closing the 90-second edge window while never touching the 3,600-second client window; and nine of fifteen decision items staying with the renter.

The last two lessons set up the endpoint the request finds and made that endpoint stable. Every
request reaching the measurement network's dashboard now arrives at the correct machine — and
every request arrives **at that machine**.

The regional map, the counter list, and the asset files travel the same path for a client far
away too. An egress fee is charged for every byte leaving the origin server, and the same map
leaves the same region thousands of times a day.

How the edge cache works, how the hit rate forms, and how freshness sets the window were measured
earlier; this lesson does not repeat that mechanics. Here the edge is a **rented service**, and
three things are measured: at which hit rate the bill turns over under two separate egress
prices, how many requests and seconds an invalidation costs, and how many of the decisions handed
to the edge stay with the renter. The hit rate here is not a result but an **axis that is swept**.

- **CN43.** The fictional regional measurement network's monthly traffic is four million requests
  and a 40 KB average response; 160 GB total. When the edge is turned on, every byte going to the
  client leaves through the edge.
- **CN44.** The pricing is fictional, the currency's name is **unit**: origin egress is nine
  units per GB, edge egress is five units per GB, the request fee is 0.4 units per ten thousand
  requests. The one point where the model rests on reality is that edge egress is cheaper than
  origin egress, but not zero.
- **CN45.** There are forty edge nodes, and an object sits on ten nodes on average; the coverage
  is fictional.
- **CN46.** The time for an invalidation to reach the slowest node is fictional: 90 seconds for
  path by path, 150 seconds for prefix. A thousand paths a month are free, beyond that it is
  0.005 units per path.
- **CN47.** In one release, 2,400 asset files change; there are 18,000 objects total under the
  prefix.
- **CN48.** The response sits in the client's own cache for the duration of the freshness period
  — 3,600 seconds, fictionally — and the invalidation never reaches the client.

```js
// edge/distribution.mjs — model: what changes when the edge is rented as a service. The cache
// mechanics and how the hit rate forms were measured earlier; here the hit rate is an axis,
// it is swept. Prices are fictional, the currency's name is "unit".
const REQUESTS_MONTH = 4e6, AVG_KB = 40;
const DATA = (REQUESTS_MONTH * AVG_KB) / 1e6;           // GB
const ORIGIN_EGRESS = 9, EDGE_EGRESS = 5, REQUEST_FEE = 0.4;   // unit/GB, unit/GB, unit/10k
const requestFee = (n) => (n / 1e4) * REQUEST_FEE;
const bill = (hit, hasEdge) => {
  const landing = Math.round(REQUESTS_MONTH * (1 - hit));
  const originGB = DATA * (hasEdge ? 1 - hit : 1);
  return { landing, originGB, origin: originGB * ORIGIN_EGRESS,
    edge: hasEdge ? DATA * EDGE_EGRESS : 0, request: hasEdge ? requestFee(REQUESTS_MONTH) : 0 };
};
const total = (f) => f.origin + f.edge + f.request;
const BASELINE = total(bill(0, false));
const E = [13, 21, 20, 15, 13, 13, 15];
const print = (h) => h.map((d, i) => String(d)[i ? "padStart" : "padEnd"](E[i])).join("");
console.log(print(["hit rate(%)", "requests to origin", "origin egress(GB)", "origin fee",
  "edge fee", "request fee", "total(unit)"]));
const row = (name, f) => console.log(print([name, f.landing, f.originGB.toFixed(1), f.origin.toFixed(1),
  f.edge.toFixed(1), f.request.toFixed(1), total(f).toFixed(1)]));
row("no edge", bill(0, false));
for (const p of [0, 40, 60, 67, 80, 90, 95]) row(String(p), bill(p / 100, true));
let breakeven = 100;
for (let p = 0; p <= 100; p += 1) if (total(bill(p / 100, true)) <= BASELINE) { breakeven = p; break; }
console.log(`\nwithout the edge, the bill is ${BASELINE.toFixed(1)} units; the hit rate where the edge passes it: ${breakeven}%`);

// --- The invalidation's hidden bill -----------------------------------------------
// In one release, 2,400 asset files change. Under the prefix there are 18,000 objects total.
// Propagation times, coverage, and fees are fictional.
const NODES = 40, COVERAGE = Math.round(NODES / 4), QUOTA = 1000, PATH_FEE = 0.005;
const CHANGED = 2400, PREFIX_OBJECTS = 18000, FRESHNESS = 3600;
// [method, invalidation requests, dropped objects, edge window, client window]
const METHOD = [
  ["path by path", CHANGED, CHANGED, 90, FRESHNESS],
  ["prefix", 1, PREFIX_OBJECTS, 150, FRESHNESS],
  ["versioned path", 0, 0, 0, 0],
];
const Y = [16, 16, 14, 18, 20, 17, 18];
const print2 = (h) => h.map((d, i) => String(d)[i ? "padStart" : "padEnd"](Y[i])).join("");
console.log("\n" + print2(["method", "requests", "fee(unit)", "edge window", "client window",
  "dropped objects", "warming requests"]));
for (const [name, requests, dropped, edgeWindow, clientWindow] of METHOD) {
  const fee = Math.max(0, requests - QUOTA) * PATH_FEE;
  const warming = (name === "versioned path" ? CHANGED : dropped) * COVERAGE;
  console.log(print2([name, requests, fee.toFixed(2), edgeWindow, clientWindow, dropped, warming]));
}

// --- The decision handed off: counting items --------------------------------------
// [group, item, whose, fixed at creation?]
const DECISION = [
  ["key", "path", "you", false], ["key", "query parameters", "you", false],
  ["key", "selected headers", "you", false], ["key", "cookie", "you", false],
  ["freshness", "freshness duration", "you", false], ["freshness", "stale object on error", "you", false],
  ["freshness", "edge's freshness ceiling", "provider", false],
  ["compression", "on or off", "you", false], ["compression", "algorithm and level", "provider", false],
  ["distribution", "origin definition", "you", false], ["distribution", "distribution name", "you", true],
  ["distribution", "node count and placement", "provider", false],
  ["distribution", "node the client lands on", "provider", false],
  ["distribution", "object evicted when memory fills", "provider", false],
  ["distribution", "propagation time", "provider", false],
];
const G = [14, 10, 9, 15, 20];
const print3 = (h) => h.map((d, i) => String(d)[i ? "padStart" : "padEnd"](G[i])).join("");
console.log("\n" + print3(["group", "item", "you", "provider", "fixed at creation"]));
const groups = [...new Set(DECISION.map((k) => k[0]))];
const count = (l) => [l.length, l.filter((k) => k[2] === "you").length,
  l.filter((k) => k[2] === "provider").length, l.filter((k) => k[3]).length];
for (const g of groups) console.log(print3([g, ...count(DECISION.filter((k) => k[0] === g))]));
console.log(print3(["total", ...count(DECISION)]));
```

```
hit rate(%)     requests to origin   origin egress(GB)     origin fee     edge fee  request fee    total(unit)
no edge                    4000000               160.0         1440.0          0.0          0.0         1440.0
0                          4000000               160.0         1440.0        800.0        160.0         2400.0
40                         2400000                96.0          864.0        800.0        160.0         1824.0
60                         1600000                64.0          576.0        800.0        160.0         1536.0
67                         1320000                52.8          475.2        800.0        160.0         1435.2
80                          800000                32.0          288.0        800.0        160.0         1248.0
90                          400000                16.0          144.0        800.0        160.0         1104.0
95                          200000                 8.0           72.0        800.0        160.0         1032.0

without the edge, the bill is 1440.0 units; the hit rate where the edge passes it: 67%

method                  requests     fee(unit)       edge window       client window  dropped objects  warming requests
path by path                2400          7.00                90                3600             2400             24000
prefix                         1          0.00               150                3600            18000            180000
versioned path                 0          0.00                 0                   0                0             24000

group               item      you       provider   fixed at creation
key                    4        4              0                   0
freshness              3        2              1                   0
compression            2        1              1                   0
distribution           6        2              4                   1
total                 15        9              6                   1
```

## Two Different Thresholds

The first table's first row is the baseline: no edge, all four million requests land on the
origin, 160 GB leaves the origin, the bill is 1,440 units.

The second row turns the edge on and holds the hit rate at zero. The bill rises to 2,400 — a 67%
increase. The reason is plain: every byte going to the client now leaves through the edge, and
edge egress is not free (800 units), plus a request fee is charged on top (160 units). Origin
egress did not drop at all, because the hit rate is zero. Turning the edge on is not by itself a
saving; it is **adding two bill items**.

In the rows below, the only item that changes is origin egress. The edge fee stays fixed at 800
units, the request fee at 160 units — these two items are not affected by the hit rate at all.
The total bill's curve is therefore different from the origin curve: there is a fixed base of 960
units, and origin savings stack on top of that base.

The **breakeven point** was computed at the end of the run: 67%. At this hit rate the total is
1,435.2 units, just below the edgeless layout's 1,440 units.

The real reading is in the 60% row. At a 60% hit rate, requests landing on the origin have
dropped from four million to 1.6 million; the origin server's load has dropped 60%, its scaling
has eased, its instance count can be lowered. The bill, though, is 1,536 units — still higher
than the edgeless layout. **Lowering origin load and lowering the bill are two different
thresholds**, and in this model the second one arrives later.

What determines the breakeven point is the ratio of the two prices. Because edge egress is five
ninths of origin egress, the share that can be saved is only a portion of the origin item. If the
two prices were equal, the breakeven point would never arrive: the fixed 800 plus 160 units would
never fall below the 1,440 units that can at most be won back on the origin side. The number the
decision rests on is not "the edge is cheap" but **how many times cheaper**.

At 90% the bill drops to 1,104 units, at 95% to 1,032 units. Against the baseline layout, the
best saving is about 28%, and in exchange for it, requests landing on the origin have dropped
twentyfold. What the edge really delivers is **origin load**, more than money; money is a
secondary gain that arrives later.

## The Invalidation's Hidden Bill

The second table measures a release: 2,400 asset files changed, and the stale copies on the edge
need to drop.

The path-by-path method sends 2,400 invalidation requests. A thousand of them are within the free
quota, the remaining 1,400 cost 0.005 units per path: seven units total. Next to the monthly bill
of 1,440 units, this is an invisible number. The window is 90 seconds, dropped objects 2,400.

The prefix method sends a single request and its fee is zero. Reading the two columns side by
side, the prefix looks superior in every respect: one request instead of 2,400, zero instead of
seven units.

The last column breaks this appearance. **All 18,000 objects under the prefix drop**, not just
the 2,400 that actually changed. Since each dropped object sits on ten nodes on average, 180,000
requests land on the origin during warming. Under the path-by-path method this number is 24,000: a
sevenfold-and-a-half difference.

The size of this number becomes visible when compared against the first table. In a month with a
90% hit rate, total requests landing on the origin were 400,000. A single prefix invalidation
produces **45%** of that month's origin load, compressed into a few minutes. The fee is zero, but
the bill is not seven units; it is written on the origin side, in the instances the scaling group
opens.

The prefix also propagates more slowly: 150 seconds against 90. The method that looks cheap is
both wider and slower.

The third row removes invalidation altogether. Under the versioned path, a changed file is
published under a new name; the old name is never invalidated, the new name is not yet on the
edge at all. Invalidation requests zero, fee zero, window zero, dropped objects zero. Warming
requests are 24,000 — only for the 2,400 files that actually changed. The cost does not show up in
the table: the old names keep sitting on the edge and in storage, and they have to be cleaned up
somewhere.

## Where Invalidation Cannot Reach

The fourth and fifth columns hold this lesson's quietest number.

The edge window in the first method is 90 seconds: after 90 seconds, the stale copy is gone from
all forty of the forty nodes. The client window, in the same row, is 3,600 seconds. A fortyfold
difference.

The reason fits in one sentence: invalidation is a message sent to the edge, and there is no
message that goes to the client. A response published with a one-hour freshness sits in the
client's own cache for an hour, and for that whole time the client makes no request at all. The 90
seconds invalidation closes is one fortieth of the real window.

This is the course's **item that cannot be taken back**: the published response. The copy on the
edge is deleted with a call, the copy on the client cannot be deleted. If a wrong price, a wrong
regional map, or a response meant for one person went out with a one-hour freshness, that response
is in circulation for an hour, and there is no switch that stops it.

The third row shows the one fix: the client window is zero, because under the versioned path the
response's name changes and the client never asks for the old name it holds. But this too is not
retroactive — a response distributed under yesterday's name still sits in the client today. The
versioned path resolves everything after this point, not before it.

A short freshness is the second fix that comes to mind, and it ties back to the first table:
shortening the freshness narrows the client window, but it also lowers the edge hit rate, and if
the hit rate drops below 67% the bill rises above the edgeless layout. Three numbers are tied at
one knot: **freshness, hit rate, bill**.

## What Was Handed to the Edge

The third table applies this course's question to this lesson. Fifteen decision items were
counted: **nine with you**, six with the provider, one fixed at creation.

All four of the key group are yours. What goes into the path, the query parameters, the selected
headers, and the cookie is your configuration line even in a rented service. That a personalized
response in a shared cache can go to someone else was measured earlier; renting the edge as a
service does not hand off that decision, it only raises the number of nodes the mistake can spread
across to forty.

In freshness, two of three items are yours. The third — the edge's own freshness ceiling — is
not: the duration you write can hit a cap, and that cap is not your decision.

In compression, one of two items is yours. You decide whether compression is on; the provider
decides which algorithm at which level. This second item ties directly back to the first table:
the compression level sets the response's byte size, the byte size sets the 160 GB total, and
therefore both the edge and origin egress fees. The bill's multiplier sits in the provider's
choice.

Only two of the distribution group's six items are yours. Node count and placement, which node
the client lands on, which object gets evicted when memory fills, and how long propagation takes —
all four directly affect the numbers this lesson measured, and all four are off the table for you.
The eviction policy in particular matters: the 67% breakeven point is your calculation, but
whether that point is actually reached is partly decided by the provider's memory management.

The one item fixed at creation is the distribution name. A distribution's name does not change
afterward; changing it means creating a new distribution and rebuilding the name integration the
previous lesson measured.

## Summary

- Turning on the edge by itself raised the bill from 1,440 to 2,400 units: edge egress and the
  request fee are independent of the hit rate and add a fixed base of 960 units.
- The bill turns over at a 67% hit rate. At 60%, requests landing on the origin had dropped from
  four million to 1.6 million, while the bill was still above the edgeless layout: lowering
  origin load and lowering the bill are two different thresholds, and the second one arrives
  later.
- Prefix invalidation sends one request at zero fee but drops 18,000 objects, bringing 180,000
  warming requests down onto the origin — 45% of a 90%-hit-rate month's origin load. Path by path
  costs seven units and produces 24,000 requests.
- Invalidation closes the 90-second edge window and never touches the 3,600-second client window.
  A published response cannot be recalled: this is the course's item that cannot be taken back,
  and the versioned path resolves only what comes after it.
- Nine of fifteen decision items stayed with the renter: all four of the key, two of freshness,
  one of compression, two of distribution. The compression level sits with the provider, and it
  sets the bill's multiplier.

## Next Step

With this lesson, the path of the request coming from outside is complete: the address space was
split, traffic was routed, filtered, balanced, its address was made stable, and it was moved to
the edge. Every step of the path serves a client arriving from the **public network**.

The measurement network's own components also send requests to each other. The collector writes
to the data warehouse, the dashboard calls the billing service, and there is also a shared
validation service sitting in another account. None of this traffic is client traffic, but today
it flows through the same path: with public addresses, over the public network, using the full
surface of the edge and the balancer. The egress fee is charged to the same column too.

Two networks can connect to each other without ever going out to the public network. The next
lesson measures this: how many items the opened surface differs by between peering two networks
directly and opening a single service with a private link, what happens when address spaces
overlap, and which part of this decision stays fixed once it is set up.
