---
title: "Counting Against the Network"
subtitle: "The safety–availability–utilization trilemma of edge rate limiting, and single-writer durable actors as a coordinator primitive"
discipline: "Computer Science"
articleType: "Research Article"
authors: ["Dima Doronin"]
keywords: ["Distributed Systems", "Edge Computing", "Rate Limiting", "Durable Objects", "Consistency", "Fault Tolerance"]
date: "2026-07-18T02:16:14.097Z"
---

# Counting Against the Network

*The safety–availability–utilization trilemma of edge rate limiting, and single-writer durable actors as a coordinator primitive*

## Abstract

Global rate limiting — admit at most N requests per key per window — is trivial in one process and one of the hardest invariants to enforce across a planet-scale edge, where the counter's state is spread over hundreds of points of presence. We formalize the problem as coordination and prove a small impossibility result: no edge limiter can be simultaneously safe (never exceed N), available under network partition (decide from local state), and fully utilizing (admit up to N), against an adversarial partition scheduler; the utilization loss forced by safety-plus-availability grows with fan-out as N(1 - 1/P). Every practical design is thus a chosen point on a trilemma with a bounded loss on the third axis. We then analyze the single-writer, key-addressed durable actor — a primitive offered in different forms by Microsoft Orleans, Azure Durable Entities, Akka Cluster Sharding, and Cloudflare Durable Objects — as the coordinator that supplies the one genuinely hard ingredient: an exact, linearizable, per-key serialization point with colocated durable storage and no application-managed consensus cluster. Given that primitive, the design reduces to selecting a position on the triangle. We give four designs with runnable code in one concrete instantiation (exact central counter, lease-based reservation, optimistic reconciliation, sharded hot-key counter) and quantify each loss: (P-1)l stranded tokens for leases, r*tau*(P-1) overshoot for reconciliation, skew-proportional under-admission for shards. We are explicit about the model's limits — the single-writer throughput ceiling and single-location residence — and tie the analysis to two 2026 developments: the Uptime Institute Annual Outage Analysis 2026, which reports connectivity failures rising and lengthening, and a per-facet storage-isolation feature that lowers the cost of per-tenant counter decomposition.

Distributed rate limiting looks deceptively simple: admit at most $N$ requests per key per time window, reject the rest. In a single process it is a counter guarded by a mutex. On a globally distributed edge — hundreds of points of presence (PoPs), each terminating traffic for the same key — the counter becomes the hardest object in the system to build, because a *global* invariant ("total admissions $\le N$") must be enforced over state that is, by construction, spread across the planet.

This manuscript treats global rate limiting as a coordination problem. We prove a small impossibility result that pins down why every practical limiter is a compromise, and then ask which coordination primitive most economically realizes each compromise. The primitive we analyze is the *single-writer, key-addressed durable actor*: a stateful object, uniquely instantiated per key across a cluster, that serializes access to its own colocated storage. This abstraction is not tied to one product — it is offered, with differing guarantees, by Microsoft Orleans "virtual actors" [1], Azure Durable Entities [2], Akka Cluster Sharding [3], and Cloudflare Durable Objects [4]. We use one concrete instantiation for runnable code, but the argument is about the abstraction, and §5 is explicit about where it fails.

*Relation to prior work.* This paper continues a line of the present author's work on entity-granularity edge coordination [15,16]; where those treat coordination and fault isolation in general terms, the contribution here is specific to admission control: a rate-limiting *trilemma* with an impossibility proof (§2) and closed-form loss bounds for four named designs (§4). No text, figure, or bound is reused from that earlier work.

The analysis is anchored in two developments from the current news cycle. First, the **Uptime Institute Annual Outage Analysis 2026** (published 13 May 2026) reports that external infrastructure and connectivity failures are a growing share of impactful outages, with fiber and connectivity issues "more likely to result in extended disruptions," and that more than half of major outages now cost over \$100,000 ([Uptime Institute, 2026](https://uptimeinstitute.com/resources/research-and-reports/annual-outages-analysis-2026)) [5,6]. Partitions between an edge PoP and its coordinator are therefore not a corner case to wave away — they are, increasingly, the modal failure. Second, an April 2026 feature for the durable-actor system used in our examples lets a single actor host many named child objects, each with its own isolated embedded-SQLite database under a shared lifecycle [7,8]. This changes the cost of *per-tenant* counter decomposition, and §6 uses it to make the per-key isolation argument concrete.

## 1. Problem statement

Fix a key $k$ (an API token, an IP, a tenant). A rate-limit policy is a predicate over the history of admissions for $k$. Two policies dominate practice.

**Fixed / sliding window.** Admit request at time $t$ iff the number of admissions in the trailing window $[t-W, t)$ is below $N$. Write $C_k(t)$ for that count; the invariant is $C_k(t) \le N$.

**Token bucket** [9]. Maintain a token level $T_k$ that refills at rate $r$ (tokens/second) up to a burst ceiling $b$. Because refill is a closed-form function of elapsed time, the level never needs a background timer:

$$T_k(t) = \min\!\Big(b,\; T_k(t_0) + r\,(t - t_0)\Big),\qquad \text{admit cost } c \iff T_k(t) \ge c,$$

after which we commit $T_k \leftarrow T_k(t) - c$ and $t_0 \leftarrow t$. Token bucket subsumes fixed-window smoothing and is what we implement below.

Now distribute it. Requests for $k$ arrive at PoPs $1, \dots, P$; let $a_i(t)$ be the arrival stream at PoP $i$. A *global* limiter must enforce the invariant on $\sum_i a_i$, not on each $a_i$ independently. Three properties are in tension:

- **(S) Safety.** In every fault-free execution, global admissions in any window never exceed $N$ (equivalently, the global token level never goes negative).
- **(A) Availability.** During a network partition, every PoP still renders an admit/reject decision from local state alone, within bounded local latency.
- **(U) Utilization.** When global demand meets or exceeds $N$, the limiter admits (close to) $N$ — it does not leave the budget on the table.

Safety is the security property (an abuse limiter that over-admits by $10\times$ under load has failed exactly when it mattered). Availability is what the edge exists to provide. Utilization is why anyone tolerates the machinery instead of setting each PoP to $N/P$ and walking away.

## 2. Why you cannot have all three

:::figure{caption="The rate-limiting trilemma. An exact central counter gives up (A); a static per-PoP split gives up (U); optimistic local admission with async reconciliation gives up (S) by a bounded amount. A single-writer coordinator lets a design sit anywhere on the S–U edge while the coordinator itself stays exact."}
```
                 (S) Safety
                 total <= N
                /          \
               /            \
     lease-based            central
     reservation            counter
     (exact, but            (exact, but
      strands capacity)      one RTT / partition-fragile)
             /                  \
            /                    \
   (U) Utilization ---- ... ---- (A) Availability
   admit up to N        optimistic local +
                        async reconcile
                        (fast + available,
                         over-admits <= epsilon)
```
:::

**Proposition (edge rate-limiting trilemma).** *No global limiter satisfies (S), (A), and (U) simultaneously against an adversarial partition scheduler.*

*Proof sketch.* Consider $P \ge 2$ PoPs and a policy with budget $N$. An adversary severs every inter-PoP link and every link to any coordinator, so each PoP must decide from local state (this is what (A) demands). Let $b_i$ be the number of admissions PoP $i$ can make during the partition using only its local pre-partition state. To satisfy (S) in the execution where the adversary drives demand at *all* PoPs at once, we need $\sum_i b_i \le N$; otherwise some execution admits more than $N$. Now the adversary instead concentrates all demand at the PoP with the smallest budget, $b_{\min} \le N/P$. That PoP admits at most $b_{\min}$, while global demand is $\ge N$ — a utilization gap of at least $N - N/P = N(1 - 1/P)$. Raising any $b_i$ to close the gap breaks the safety sum. Hence (S) $\wedge$ (A) forces a utilization loss that grows with $P$. $\square$

This is the CAP theorem's structure [10] specialized to a counting invariant, and the takeaway is the same: *pick the two properties you can defend, and bound your loss on the third.* Every design in §4 is a named point on this triangle. The role of a single-writer coordinator is narrow but decisive: it provides an *exact and linearizable* counter without the application operating a consensus cluster, so the engineering budget is spent on *where to sit on the triangle* rather than on building the counter correctly.

## 3. The single-writer durable actor as an abstraction

Independent of any product, the primitive we rely on has three properties.

1. **Single-instance serialization.** For a given class and identity, at most one live instance exists cluster-wide, and it processes events one at a time. A read–modify–write on the counter is therefore atomic without application locks: the instance *is* the mutex the single-process case had. In Cloudflare Durable Objects this is realized by *input gates* (holding new events while synchronous code runs) and *output gates* (holding writes until confirmed) [4,11]; Orleans obtains it from single-activation virtual actors with turn-based concurrency [1]; Akka from cluster-sharded entities with a single owning node [3].
2. **Colocated durable storage.** The instance owns local, transactional storage reached without a network hop — embedded SQLite in the Cloudflare case [12], a state store behind Durable Entities [2], or the actor's persisted event log in Akka [3]. Counter and mutating code live in the same place.
3. **Identity-addressed placement.** A key deterministically names an instance (`idFromName(k)` and `get(id)` in the Cloudflare API [4]; grain identity in Orleans; entity id in Akka). Distinct keys land on distinct instances, so the natural unit of a rate limiter — one counter per key — is also the natural unit of the actor system, and idle keys can hibernate at near-zero cost.

The following table situates the instantiation used for code against its peers.

| System | Single-activation guarantee | Colocated storage | Idle cost |
|---|---|---|---|
| Cloudflare Durable Objects [4] | input/output gates | embedded SQLite [12] | hibernates |
| Microsoft Orleans [1] | single-activation virtual actor | external grain state | deactivates |
| Azure Durable Entities [2] | single-threaded entity | durable task state store | dehydrates |
| Akka Cluster Sharding [3] | one owning shard region | persistent event log | passivates |

The abstraction's cost, made explicit in §5, is intrinsic and shared by all four: the instance lives in *one* location at a time and executes on *one* logical thread. It is a point of serialization, with a point's throughput and a point's failure domain. The remainder uses the Cloudflare instantiation because its embedded-SQLite storage makes the code self-contained, not because the analysis is specific to it.

## 4. Designs, and where each sits on the triangle

### 4.1 Exact central counter (sacrifices A)

The canonical single-writer token bucket. Every request routes to the key's actor; the actor holds the authoritative token level and answers yes/no. In a fault-free execution it is exactly safe and fully utilizing, and it is the coordinator all other designs lean on.

```typescript
export interface Env {
  LIMITER: DurableObjectNamespace<TokenBucket>;
}

// A single-writer token bucket. One instance per key, cluster-wide.
export class TokenBucket extends DurableObject<Env> {
  private tokens!: number;     // in-memory authoritative level
  private lastRefill!: number; // ms epoch of last refill
  private readonly rate = 100;  // tokens/sec
  private readonly burst = 100; // ceiling

  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    // Serialize initialization before any request is delivered.
    ctx.blockConcurrencyWhile(async () => {
      ctx.storage.sql.exec(
        `CREATE TABLE IF NOT EXISTS bucket(id INTEGER PRIMARY KEY, tokens REAL, ts INTEGER)`
      );
      const row = ctx.storage.sql
        .exec<{ tokens: number; ts: number }>(`SELECT tokens, ts FROM bucket WHERE id = 0`)
        .toArray()[0];
      this.tokens = row?.tokens ?? this.burst;
      this.lastRefill = row?.ts ?? Date.now();
    });
  }

  // Consume `cost` tokens if available. Returns the decision.
  // The output gate holds the response until confirmed writes flush, so persisting
  // the decrement here commits it before the caller observes the admit (strict safety).
  async consume(cost = 1, now = Date.now()):
      Promise<{ ok: boolean; remaining: number; retryAfterMs: number }> {
    const elapsed = Math.max(0, now - this.lastRefill) / 1000;
    this.tokens = Math.min(this.burst, this.tokens + elapsed * this.rate);
    this.lastRefill = now;

    if (this.tokens >= cost) {
      this.tokens -= cost;
      this.persist(now);
      return { ok: true, remaining: Math.floor(this.tokens), retryAfterMs: 0 };
    }
    const deficit = cost - this.tokens;
    return { ok: false, remaining: Math.floor(this.tokens), retryAfterMs: Math.ceil((deficit / this.rate) * 1000) };
  }

  private persist(now: number): void {
    this.ctx.storage.sql.exec(
      `INSERT INTO bucket(id, tokens, ts) VALUES (0, ?, ?)
       ON CONFLICT(id) DO UPDATE SET tokens = excluded.tokens, ts = excluded.ts`,
      this.tokens, now
    );
  }
}
```

The request-handling front end:

```typescript
export default {
  async fetch(req: Request, env: Env): Promise<Response> {
    const key = req.headers.get("cf-connecting-ip") ?? "anon";
    const stub = env.LIMITER.get(env.LIMITER.idFromName(key));
    const { ok, retryAfterMs } = await stub.consume(1);
    if (!ok) {
      return new Response("rate limited", {
        status: 429,
        headers: { "retry-after": String(Math.ceil(retryAfterMs / 1000)) },
      });
    }
    return fetch(req); // forward to origin
  },
} satisfies ExportedHandler<Env>;
```

Two subtleties concern durability and clocks. Within a fault-free run the in-memory level is authoritative and access is serialized, so the counter is exactly safe by construction. The persistence strategy then sets a knob between safety and throughput. As written, the decrement is persisted *inside the output gate before the admit is returned*, so the write commits on the safe path and an instance crash cannot forget an already-consumed token — strict safety, at the cost of one storage round trip per admitted request. If instead persistence is made opportunistic (batched on an alarm), a crash between flush points can forget a few consumed tokens and thus admit slightly *over* budget on recovery. That is a deliberate, bounded fail-*open*: the over-admission is at most the unpersisted delta, and for abuse control admitting a handful of extra requests after a crash is the tolerable failure direction. A hard security boundary should keep the durable path; a fairness guard can take the throughput. Either way, time comes from the local wall clock — monotone-enough for refill against *this* instance's own `lastRefill`, but never compared across instances, since these systems do not offer a synchronized global clock.

**Cost.** Every admitted request pays one round trip to the key's home location. For a European user whose key is homed in North America, that is a trans-Atlantic RTT (~80 ms) on the critical path, and if the region is partitioned from the user's PoP the request cannot be decided at all — precisely the connectivity-failure mode the Uptime 2026 report flags as increasingly common and increasingly long-lived [5]. This design gives up (A).

### 4.2 Lease-based reservation (keeps S, trades U for latency)

Amortize the round trip. A PoP asks the coordinator for a *lease* of $\ell$ tokens, then serves the next up-to-$\ell$ requests locally, returning only when the lease is spent or expires. The coordinator hands out leases summing to at most the true budget, so **safety is preserved exactly**: global admissions $\le$ total leased $\le N$.

$$\text{admitted} \;\le\; \sum_{i=1}^{P} \text{leased}_i \;\le\; N.$$

The price is utilization. A PoP holding an unused lease of size $\ell$ has *stranded* that capacity: the global count may be below $N$ while some PoP rejects. The worst-case stranded capacity — the utilization gap from §2 made quantitative — is

$$\text{stranded} \;\le\; (P - 1)\,\ell,$$

when $P-1$ PoPs each hold a full unused lease and all demand hits the $P$-th. So $\ell$ is the tuning knob on the S–U edge: large $\ell$ means few round trips (one per $\ell$ requests) but coarse global accuracy; small $\ell$ means fine accuracy but the round trip reappears. Lease expiry (a TTL, reclaimed by a coordinator alarm) bounds how long a dead PoP can strand capacity.

```typescript
// PoP-local lease cache. One entry per key per PoP (in isolate memory).
interface Lease { tokens: number; expiresAt: number; }
const leases = new Map<string, Lease>();

async function admit(key: string, env: Env, now = Date.now()): Promise<boolean> {
  const L = leases.get(key);
  if (L && L.tokens >= 1 && L.expiresAt > now) {
    L.tokens -= 1;
    return true; // fast path: zero coordination
  }
  const stub = env.LIMITER.get(env.LIMITER.idFromName(key));
  const granted = await stub.lease(64); // ask for a chunk of 64 tokens
  if (granted.tokens <= 0) return false;
  granted.tokens -= 1;
  leases.set(key, granted);
  return true;
}
```

On the coordinator, `lease(n)` is `consume(n)` with a TTL stamped on the grant; an `alarm()` handler credits expired, unspent leases back to the bucket so a crashed PoP does not permanently strand its chunk.

### 4.3 Optimistic local + async reconciliation (trades S by a bounded ε for A and U)

When coordination latency cannot be paid on the hot path and small overshoot is tolerable, invert the flow: each PoP admits against a local sub-budget and *reports* consumption to the coordinator every $\tau$ seconds; the coordinator periodically *rebalances* sub-budgets toward observed demand — an accounting relationship close to a bounded-error CRDT counter [13]. This is the availability corner — decisions are always local — at the cost of bounded over-admission.

If the global rate is $r$ and reports lag by up to $\tau$, then between reconciliations the PoPs can collectively out-run the true budget only by what they consume in the blind interval. A defensible bound, under the assumption that a PoP stops admitting once its own sub-budget is exhausted, is

$$\varepsilon \;\le\; r\,\tau\,(P-1),$$

i.e. over-admission is linear in the reconciliation interval and in fan-out. Halving $\tau$ halves the overshoot and doubles the report traffic — the same knob, now on the S–A edge. This is the regime most "approximate" edge limiters occupy [14], and it is the correct one when the policy is a *fairness* guard rather than a hard security boundary.

### 4.4 Sharded counter for a hot key (keeps S, trades U under skew)

§5 shows a single actor tops out at one core's throughput. For a key hotter than that, split it into $S$ shard-actors, each enforcing $N/S$, and route request $j$ to shard $h(j) \bmod S$. Global safety survives — $\sum_{s} N/S = N$ — but a shard is only safe for the slice of traffic it sees, so **skew costs utilization**: if traffic concentrates on a few shards, those throttle at $N/S$ while other shards sit idle. The mitigation is a two-level scheme (shards lease from a parent aggregator, §4.2 recursively), the structure §6 makes cheap.

```typescript
// Fan-out admission across S shard-actors for one very hot key.
async function admitHot(key: string, S: number, env: Env): Promise<boolean> {
  const shard = hash32(key + crypto.randomUUID()) % S; // spread load across shards
  const stub = env.LIMITER.get(env.LIMITER.idFromName(`${key}:shard:${shard}`));
  const { ok } = await stub.consume(1);
  return ok;
}
```

## 5. Failure modes and honest limits

**The single-writer throughput ceiling.** The instance is one logical thread. An in-memory counter serves on the order of $10^4$ decisions/second before the event queue becomes the bottleneck; the strict-durable path of §4.1, which awaits a storage write per admit, is lower. For keys below that ceiling — the overwhelming majority — a per-key actor is ideal. Above it, sharding (§4.4) is mandatory and re-enters the approximation regime. The abstraction does not repeal the physics of a single serialization point; it makes that point cheap, durable, and correctly serialized, which is a different and lesser claim.

**Single-location residence and partitions.** A key's actor lives in one location. A PoP partitioned from it — the Uptime-2026 connectivity failure [5] — cannot reach the exact counter. The design must pre-commit to **fail-open** (admit, protecting availability, risking abuse) or **fail-closed** (reject, protecting the origin, risking a self-inflicted outage). The lease design (§4.2) softens this: a PoP holding a live lease keeps serving through a partition of bounded duration $=$ lease TTL, converting a hard outage into graceful degradation. Against the failure class the 2026 data highlights, this is the most consequential design move the model enables.

**Cold start and clocks.** The first request to a cold key pays instantiation latency. Refill arithmetic trusts the local clock within a single instance only; we never compare timestamps *across* instances, a discipline that also protects the sliding-window variant from clock-skew miscounts.

**Storage write amplification.** Persisting on every request serializes the bucket behind disk; this is exactly the cost of the strict-durable path in §4.1. The opportunistic-persist variant keeps the exact design fast, its price being the bounded fail-open window on crash discussed there. The choice is a policy decision, not an implementation detail.

### Where the abstraction helps vs. where it does not

| Sub-problem | Single-writer actor is a strong fit | It is not enough |
|---|---|---|
| Per-key exact counting | ✔ one instance = one mutex + store | — |
| Millions of mostly-idle keys | ✔ hibernation, identity-as-sharding | — |
| A single key above ~$10^4$ rps | — | need sharding → approximation |
| Sub-RTT global decision across oceans | — | physics: instance is in one location |
| Partition-tolerant *exact* limit | — | forbidden by §2; leases only soften it |
| Per-tenant isolation of limiter state | ✔ especially with per-facet storage (§6) | — |

## 6. Per-tenant isolation as a first-class boundary

A recurring requirement in multi-tenant limiting is that a tenant's usage counters are simultaneously a *security boundary* (one tenant must never observe or corrupt another's budget) and an *aggregation point* (a plan-wide limit is the sum over that tenant's per-endpoint counters). The naive realization — one actor per (tenant, endpoint) plus a directory to enforce the plan cap — is workable but operationally heavy.

An April 2026 feature of the durable-actor system used here lets a single actor host many named children, each with its own **isolated embedded-SQLite database** under a shared lifecycle, created or resumed by name; the parent cannot read a child's database and vice versa, with isolation enforced by the runtime rather than by convention [7,8]. Mapped onto limiting, a supervisor actor per tenant, with one child per endpoint, gives isolated storage per counter under a single parent that enforces the plan-level cap and hands out leases (§4.2) to the children below it — the two-level scheme of §4.4 with runtime-enforced isolation between tenants.

```typescript
// Supervisor actor: one per tenant. Each endpoint counter is an isolated child.
export class TenantLimiter extends DurableObject<Env> {
  async consume(endpoint: string, cost = 1): Promise<boolean> {
    // Resume or start the per-endpoint child; it has its own SQLite DB,
    // invisible to other endpoints and to this supervisor.
    const child = this.ctx.facets.get(`ep:${endpoint}`, () => ({
      $: { className: "TokenBucket" }, // load the limiter class into the child
    }));
    const { ok } = await child.consume(cost);
    // The supervisor still owns the plan-wide cap and lease policy across children.
    return ok && this.withinPlanBudget(cost);
  }

  private withinPlanBudget(cost: number): boolean {
    // Aggregate plan-level accounting lives in the supervisor's own DB.
    // ... exact single-writer check across the tenant's endpoints ...
    return true;
  }
}
```

The feature does not change the trilemma — the parent is still one serialization point in one location — but it lowers the cost of the decomposition that best exploits the abstraction's isolation. Comparable isolation can be assembled in the other systems of §3 (separate grains, separate entities, separate persistent entities); the difference is packaging, not capability.

## 7. Conclusion

Global rate limiting is a coordination problem wearing a counter's clothes. The trilemma of §2 says no edge limiter can be simultaneously safe, available under partition, and fully utilizing; every real design is a chosen point on that triangle with a bounded loss on the third axis. A single-writer durable actor does not evade the trilemma — nothing can — but it supplies the ingredient that is genuinely hard to build correctly: an exact, linearizable, per-key serialization point with colocated durable storage and no application-managed consensus cluster. Given that primitive, the design reduces to selecting a position on the triangle (exact-central, lease-based, optimistic-reconciled, or sharded) and quantifying the loss — $(P-1)\ell$ stranded tokens for leases, $r\tau(P-1)$ overshoot for reconciliation, skew-proportional under-admission for shards. The 2026 outage data argues for designs that degrade gracefully through connectivity failures, which favors leases over the naive central counter; recent per-facet storage isolation makes the per-tenant decomposition that best exploits the model nearly free. The limiter that once needed a mutex now needs a single-writer object — and the interesting work moves from *building the counter* to *choosing what to give up when the network gives out*.

## References

1. Bernstein, P. et al. *Orleans: Distributed Virtual Actors for Programmability and Scalability.* Microsoft Research Technical Report MSR-TR-2014-41, 2014.
2. Burckhardt, S. et al. *Durable Functions: Semantics for Stateful Serverless.* Proc. ACM Program. Lang. (OOPSLA), 2021.
3. Lightbend. *Akka Cluster Sharding — documentation.* https://doc.akka.io/docs/akka/current/typed/cluster-sharding.html
4. Cloudflare. *Durable Objects — Overview.* https://developers.cloudflare.com/durable-objects/
5. Uptime Institute. *Annual Outage Analysis 2026.* Published 13 May 2026. https://uptimeinstitute.com/resources/research-and-reports/annual-outages-analysis-2026
6. Data Centre Solutions. *Uptime publishes Annual Outage Analysis Report 2026.* 2026. https://datacentre.solutions/news/22384-uptime-publishes-annual-outage-analysis-report-2026
7. Cloudflare. *Durable Objects in Dynamic Workers: give each app its own database (Facets).* April 2026. https://blog.cloudflare.com/durable-object-facets-dynamic-workers/
8. Cloudflare. *Durable Object Facets — Dynamic Workers documentation.* 2026. https://developers.cloudflare.com/dynamic-workers/usage/durable-object-facets/
9. Tanenbaum, A. and Wetherall, D. *Computer Networks* (traffic shaping: leaky and token bucket), 5th ed., Pearson, 2011.
10. Gilbert, S. and Lynch, N. *Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services.* ACM SIGACT News 33(2), 2002.
11. Cloudflare. *Rules of Durable Objects (input/output gates).* https://developers.cloudflare.com/durable-objects/best-practices/rules-of-durable-objects/
12. Cloudflare. *SQLite-backed Durable Object Storage.* https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/
13. Shapiro, M. et al. *Conflict-Free Replicated Data Types.* Proc. SSS, LNCS 6976, 2011.
14. *Designing Scalable Rate Limiting Systems: Algorithms, Architecture, and Distributed Solutions.* arXiv:2602.11741, 2026. https://arxiv.org/html/2602.11741
15. Doronin, D. *Coordination at the Edge.* openscience.tech, 2026.
16. Doronin, D. *Per-Entity Coordination at the Edge.* openscience.tech, 2026.
