Performance
On Cloudflare, performance is cost. Meter every request, find expensive queries, and stay inside free-tier limits.
#Performance
On Cloudflare, performance and cost are the same number. You don't pay for servers — you pay for what each request consumes: database rows read, CPU milliseconds, KV operations, Durable Object calls. A slow query isn't only slow; it reads more rows, and rows read are what D1 bills.
EmuView meters every request. Each D1 query, KV operation, Durable Object call, and R2 operation is counted per request and attributed to the endpoint, collection, and project that caused it:
flowchart LR
Req["API request"] --> Meter["Metering proxy<br>counts D1, KV, DO, R2 ops"]
Meter --> Handler["Route handler"]
Handler --> WAE[("Workers Analytics Engine<br>per-request datapoint")]
Handler --> Resp["Response"]
Resp -.->|when enabled| Cost["X-Cost receipt header"]
WAE --> Dash["Performance tab<br>in System Health"]The metering proxy wraps the Worker's bindings at the start of each request, so every storage operation is counted no matter which handler makes it. When the response is sent, the totals are written to Workers Analytics Engine as one datapoint. The numbers surface in three places:
| Where | What it's for |
|---|---|
| The Performance tab in System Health | Production trends, free-tier fuel gauges, and drill-down to individual expensive requests |
The X-Cost response header |
A per-request receipt for debugging and profiling — see exactly what one call consumed |
The cost:profile CLI harness |
Repeatable before/after measurements with budgets and a checked-in baseline |
#Why this matters
Cloudflare's free tier is generous but finite: 5 million D1 rows read per day, 100,000 worker requests per day, 100,000 KV reads per day. A single unindexed filter on a large collection can read thousands of rows per request — one busy endpoint can consume a day's quota before lunch. The metering exists so you find that endpoint before the quota does.
The most common fix is also the cheapest: an index. A filtered query that scans 5,000 rows to return 25 drops to reading ~25 rows once the filtered field is indexed. That's a 200× cost reduction from one schema change.
#Pages in this section
- Cloudflare costs — what each resource costs, the free-tier limits, and how EmuView's architecture maps to them
- The Performance dashboard — fuel gauges, the groups table, read amplification, and drilling down to single events
- Cost receipts — the
X-Costheader: format, how to enable it, and why it's permission-gated - Profiling — the
cost:profile/cost:diff/cost:baselineworkflow and the release ritual - Index tuning — turning a bad amplification number into an index, and verifying the fix
- Hints reference — every hint the dashboard can raise, its trigger, and what to do about it