The bill, and serverless execution
What does a distributed execution model really charge, and what changes when every operator is a function?
The bill has three items
- A granularity mismatch. A message costs 100 ns at best inside one process, and 10–100 µs across machines, against about 1 ns for a function call. Any interaction that was going to be fine-grained is now 2 to 5 orders of magnitude more expensive. So the actor model is a poor query execution model and a good substrate model: it loses badly if the message is a tuple, and wins if the message is a task.
- No barrier means no consistent snapshot. Getting one needs an explicit distributed-snapshot protocol (a way to record a consistent global state while everything keeps running). That is a debt incurred here and paid in the topic of distributed coordination.
- Mailboxes are unbounded by default, so the canonical actor failure is memory exhaustion in a mailbox. The producer owns the decision to send, and the consumer cannot refuse, so the system must add explicit backpressure or die. This is the third independent confirmation, in this article, of the principle that control flow is flow control. Erlang, Akka and Ray all grew the same mechanism for the same reason.
Where it genuinely wins: long-lived, stateful, irregular, per-key work with low fan-in: session and connection state, per-entity simulation, model serving. In data engineering it appears almost only beneath a dataflow API, not as one. The honest framing is that it is a distribution model that some data systems use to implement their runtime. It is not a peer of the iterator model.
Serverless / function-per-operator execution
In serverless execution, every worker is a short-lived function that the platform starts for you. The platform imposes four constraints, and all of them are properties of the platform, not of computation:
- No worker-to-worker networking (no inbound connections, short-lived addresses).
- A wall-clock execution cap (a function may only run for some minutes).
- No state across invocations.
- Cold-start latency (the delay of starting a function that is not already warm).
Each one forces something.
No direct networking forces all exchange through external storage. This is where the key division of Three idlenesses does its work. Bytes per connection = total shuffle bytes ÷ (M × R).
The request arithmetic is decisive. At roughly $5 per million write requests (per-request pricing), the request cost per gigabyte is
Data table
| Object size | Request cost ($ per GB) |
|---|---|
| 1,000 B | 5 |
| 1.16 KB | 4.21 |
| 1.38 KB | 3.54 |
| 1.64 KB | 2.98 |
| 1.95 KB | 2.51 |
| 2.32 KB | 2.11 |
| 2.75 KB | 1.77 |
| 3.27 KB | 1.49 |
| 3.89 KB | 1.26 |
| 4.62 KB | 1.06 |
| 5.49 KB | 0.889 |
| 6.53 KB | 0.748 |
| 7.76 KB | 0.629 |
| 9.22 KB | 0.53 |
| 11 KB | 0.446 |
| 13 KB | 0.375 |
| 15.5 KB | 0.315 |
| 18.4 KB | 0.265 |
| 21.9 KB | 0.223 |
| 26 KB | 0.188 |
| 30.9 KB | 0.158 |
| 36.7 KB | 0.133 |
| 43.6 KB | 0.112 |
| 51.8 KB | 0.094 |
| 61.6 KB | 0.079 |
| 73.2 KB | 0.067 |
| 87 KB | 0.056 |
| 103 KB | 0.047 |
| 123 KB | 0.04 |
| 146 KB | 0.033 |
| 174 KB | 0.028 |
| 206 KB | 0.024 |
| 245 KB | 0.02 |
| 292 KB | 0.017 |
| 346 KB | 0.014 |
| 412 KB | 0.012 |
| 489 KB | 0.01 |
| 582 KB | 0.008 |
| 691 KB | 0.007 |
| 822 KB | 0.006 |
| 977 KB | 0.005 |
| 1.13 MB | 0.004 |
| 1.35 MB | 0.004 |
| 1.6 MB | 0.003 |
| 1.9 MB | 0.003 |
| 2.26 MB | 0.002 |
| 2.69 MB | 0.002 |
| 3.19 MB | 0.001 |
| 3.8 MB | 0.001 |
| 4.51 MB | 0.001 |
| 5.36 MB | 0.001 |
| 6.37 MB | 0.001 |
| 7.58 MB | 0.001 |
| 9 MB | 0.001 |
| 10.7 MB | 0 |
| 12.7 MB | 0 |
| 15.1 MB | 0 |
| 18 MB | 0 |
| 21.4 MB | 0 |
| 25.4 MB | 0 |
| 30.2 MB | 0 |
| 35.8 MB | 0 |
| 42.6 MB | 0 |
| 50.6 MB | 0 |
| 60.2 MB | 0 |
| 71.5 MB | 0 |
| 85 MB | 0 |
| 101 MB | 0 |
| 120 MB | 0 |
| 143 MB | 0 |
| 170 MB | 0 |
| 202 MB | 0 |
| 240 MB | 0 |
| 285 MB | 0 |
| 338 MB | 0 |
| 402 MB | 0 |
| 478 MB | 0 |
| 568 MB | 0 |
| 675 MB | 0 |
| 802 MB | 0 |
| 954 MB | 0 |
- At 100 KB objects (10⁻⁴ GB): $5 ÷ (10⁶ × 10⁻⁴) = $0.05 per GB.
- At 10 KB objects (10⁻⁵ GB): $5 ÷ (10⁶ × 10⁻⁵) = $0.50 per GB.
Compare that with effectively nothing for a byte moved over a network socket. Request cost scales as M × R, while byte cost scales with the data. So past some width, requests dominate the whole cost of the query. That single inequality is why every serious serverless-analytics design adds either a tree-structured exchange (to reduce M × R) or an ephemeral fast intermediate store. It also predicts that pure function-per-operator (one function per operator instance, talking pairwise) stays rare, which it has. What actually ships is function-per-task, with a full pipeline inside each function.
The cap and the lack of state force each task to be self-contained and idempotent (safe to repeat). That is fine. Cold start forces either provisioned warmth (paying for idleness you were trying to avoid) or acceptance of a latency floor.
What serverless buys. About 1,000 workers in about a second, compared with minutes for a cluster. That is not a performance property. It is an elasticity property, and the trade is "costs are moved" in the money dimension. Per unit of compute, function pricing runs several times VM pricing: roughly $0.06 per GB-hour against about $0.01 for an on-demand virtual machine, so 3–8× depending on shape. Serverless is therefore a bet that your utilization is low enough that 5× the unit price on the seconds you use beats 1× the price on the hours you do not. For bursty, short, narrow-shuffle work that bet wins. For a steadily loaded warehouse it loses. State the type of claim clearly: this is economic contingency, not a permanent constraint. The price ratio is a pricing decision and has moved before.