|
| 1 | +--- |
| 2 | +status: mixed — management-plane quota (shipped) + edge (backoff) & single-tenant (proposed) |
| 3 | +--- |
| 4 | + |
| 5 | +# Quota Edge Integration: Routing ResourceClaims to Project Control Planes |
| 6 | + |
| 7 | +> The management-plane claim-routing design is **implemented and shipped** (last |
| 8 | +> verified 2026-05-26). The edge-cell and flat single-tenant designs below are |
| 9 | +> **proposed, not yet implemented**. Fixes |
| 10 | +> [#171](https://github.com/datum-cloud/compute/issues/171). |
| 11 | +
|
| 12 | +## Table of Contents |
| 13 | + |
| 14 | +- [Summary](#summary) |
| 15 | +- [Problem Statement](#problem-statement) |
| 16 | +- [Three deployment topologies](#three-deployment-topologies) |
| 17 | +- [Design](#design) |
| 18 | + - [The live watch runs where claims are co-located](#the-live-watch-runs-where-claims-are-co-located) |
| 19 | + - [Claim CRUD routes to the owning ledger](#claim-crud-routes-to-the-owning-ledger) |
| 20 | + - [The backoff safety net](#the-backoff-safety-net) |
| 21 | + - [Flat single-tenant: co-located quota and fixed-project identity](#flat-single-tenant-co-located-quota-and-fixed-project-identity) |
| 22 | +- [Alternatives considered](#alternatives-considered) |
| 23 | +- [RBAC dependency](#rbac-dependency) |
| 24 | +- [Known limitation: cell-wide unique Instance names](#known-limitation-cell-wide-unique-instance-names) |
| 25 | +- [Rollout](#rollout) |
| 26 | +- [Risks](#risks) |
| 27 | +- [Open questions](#open-questions) |
| 28 | +- [Out of Scope](#out-of-scope) |
| 29 | + |
| 30 | +--- |
| 31 | + |
| 32 | +## Summary |
| 33 | + |
| 34 | +Datum operators want to enforce quota on the Instances their customers run — and not |
| 35 | +only in the central management plane, but wherever compute runs: on **edge cells** |
| 36 | +close to users and on **flat single-tenant clusters**. Today quota enforcement works |
| 37 | +only in the central management plane. Turn it on anywhere else and one of two bad |
| 38 | +things happens: quota silently never evaluates (claims are recorded where the quota |
| 39 | +system never reads them, so every Instance runs ungated), or compute-manager crashes |
| 40 | +outright ([#171](https://github.com/datum-cloud/compute/issues/171)). |
| 41 | + |
| 42 | +This RFC makes quota enforcement work across all three deployments, so an operator can |
| 43 | +stand up a quota-enforced edge cell or single-tenant cluster and have customer |
| 44 | +Instances gated on real quota grants. The shipped half routes each Instance's quota |
| 45 | +claim to the control plane that owns the project's quota ledger and watches those claims |
| 46 | +where the manager already engages them. The proposed half extends enforcement to two |
| 47 | +more deployments: **flat single-tenant** clusters, where the ledger is co-located and |
| 48 | +watched directly; and **edge cells**, where the ledger is remote and enforcement rides |
| 49 | +the always-on backoff reconcile rather than a live watch. |
| 50 | + |
| 51 | +--- |
| 52 | + |
| 53 | +## Problem Statement |
| 54 | + |
| 55 | +**What the operator saw.** Enabling quota outside the management plane did nothing |
| 56 | +useful, or worse. In the best case every Instance sat waiting on a quota grant that |
| 57 | +never came, with no error and no signal — because the claim recording the Instance's |
| 58 | +demand was written somewhere the quota system never reads. In the worst case (edge |
| 59 | +cells) compute-manager failed to start at all: it tried to watch for quota claims on a |
| 60 | +cluster that doesn't host the quota API, and crashed on startup. |
| 61 | + |
| 62 | +Both failures share one root: the system assumed quota claims live on, and are watched |
| 63 | +from, whatever cluster the controller happens to run on. That assumption holds only in |
| 64 | +the management plane. The design below replaces it with an explicit model of where each |
| 65 | +deployment's quota ledger actually lives, and of how each learns that a claim has been |
| 66 | +granted. |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +## Three deployment topologies |
| 71 | + |
| 72 | +Quota enforcement spans three deployments, each serving a different customer shape, |
| 73 | +keeping its quota ledger in a different place, and learning about grants differently: |
| 74 | + |
| 75 | +| Topology | Serves | Where the quota ledger / ResourceClaims live | How grants are observed | |
| 76 | +|---|---|---|---| |
| 77 | +| **Management plane** | Datum's central control plane, across many customer projects | Per-project Milo control planes, which the manager already engages | Live direct watch | |
| 78 | +| **Edge cell** | Compute close to users; ledger back in a remote Milo | Remote Milo, per-project control planes — **not** on the cell | **Periodic backoff reconcile — no live watch** | |
| 79 | +| **Flat single-tenant** | A single customer's own cluster, no central Milo in the path | Co-located on the one cluster | Live direct watch | |
| 80 | + |
| 81 | +The management-plane topology is shipped; the other two are the proposed additions. The |
| 82 | +distinction that matters architecturally is whether the claims sit on a cluster the |
| 83 | +manager already watches. Where they do (management plane, single-tenant) enforcement can |
| 84 | +observe grants live; where they don't (edge) it relies on the backoff floor. |
| 85 | + |
| 86 | +--- |
| 87 | + |
| 88 | +## Design |
| 89 | + |
| 90 | +### The live watch runs where claims are co-located |
| 91 | + |
| 92 | +The core decision: **a live claim watch runs only on the clusters the manager already |
| 93 | +engages that serve the quota API** — the management plane's per-project provider |
| 94 | +clusters, and a flat single-tenant cluster's local API. We do **not** stand up |
| 95 | +additional watches against clusters the manager isn't already engaging. |
| 96 | + |
| 97 | +An edge cell's ledger fails that test twice over: its claims are remote, and they are |
| 98 | +split across one control plane per project the cell serves. So an edge cell registers no |
| 99 | +live claim watch — which is also exactly what stops the crash, since its local cluster |
| 100 | +does not serve the quota API and never should have been asked to watch that kind: |
| 101 | + |
| 102 | +> watch claims live ⟺ a cluster the manager already engages serves the quota API |
| 103 | +
|
| 104 | +A transient inability to determine this must **fail loud** (refuse to start) rather than |
| 105 | +silently resolve to "off," which would masquerade as a healthy deployment. |
| 106 | + |
| 107 | +### Claim CRUD routes to the owning ledger |
| 108 | + |
| 109 | +Creating, reading, and deleting an Instance's claim targets the control plane that owns |
| 110 | +the project's quota ledger: the project control plane in the management-plane and edge |
| 111 | +topologies, the local cluster in flat single-tenant. Whatever observes a grant must read |
| 112 | +from the same ledger the claim was written to — if writes and reads target different |
| 113 | +ledgers, claims are created in one place and looked for in another, so grants are never |
| 114 | +seen even though everything appears wired. |
| 115 | + |
| 116 | +At the edge this routing is also how enforcement stays live without a watch: each |
| 117 | +backoff reconcile creates-or-reads the Instance's remote claim and checks whether it has |
| 118 | +been granted. |
| 119 | + |
| 120 | +### The backoff safety net |
| 121 | + |
| 122 | +Enforcement has always had an always-on floor: while an Instance's quota is not yet |
| 123 | +granted, its reconcile re-runs on a backing-off schedule and re-checks the claim each |
| 124 | +time. This floor is **universal** — every topology falls back to it — and at the **edge |
| 125 | +it is the sole liveness mechanism**. Grants there are observed within seconds and |
| 126 | +self-heal as the schedule backs off; there is simply no live watch to short-circuit the |
| 127 | +wait. |
| 128 | + |
| 129 | +The tradeoff is latency, not correctness. An Instance is never ungated until its quota |
| 130 | +is actually granted; the only cost of relying on backoff is how quickly a grant is |
| 131 | +*noticed*. That cost is acceptable at the edge, which is why the live edge watch is |
| 132 | +deferred (see [Alternatives considered](#alternatives-considered)). |
| 133 | + |
| 134 | +### Flat single-tenant: co-located quota and fixed-project identity |
| 135 | + |
| 136 | +A flat single-tenant deployment co-locates the quota API and compute on one cluster, so |
| 137 | +the manager already engages the cluster that holds the claims — the live watch runs |
| 138 | +there directly, with no remote hop. It is reached simply by letting the watch run when |
| 139 | +the quota API is present locally (the decision above). |
| 140 | + |
| 141 | +The one genuinely new concept is **identity**. In the management-plane and edge |
| 142 | +topologies the project that owns a claim is derived from platform namespace-ownership |
| 143 | +labels. A flat single-tenant deployment may not run that platform labeling layer, so it |
| 144 | +instead names its single project explicitly through configuration, and every claim is |
| 145 | +attributed to that project. If local quota is enabled without a configured project, the |
| 146 | +deployment refuses to start rather than emit claims the evaluator cannot attribute. |
| 147 | + |
| 148 | +--- |
| 149 | + |
| 150 | +## Alternatives considered |
| 151 | + |
| 152 | +- **A live edge watch via a per-project-control-plane forwarder.** Considered and |
| 153 | + **deferred**. Because an edge cell's claims are split across one control plane per |
| 154 | + project it serves, a live edge watch means establishing and maintaining a watch |
| 155 | + against *each* of those control planes — fan-out that grows with the projects a cell |
| 156 | + serves. The always-on backoff already delivers grants within seconds, so the machinery |
| 157 | + isn't worth it now. Revisit if edge grant latency ever becomes a product concern. |
| 158 | +- **A single unified multi-cluster provider spanning the cell and the remote ledger.** |
| 159 | + Rejected: folding the remote ledger into the cell's own provider destabilizes the |
| 160 | + cell's control path — it breaks the single-cluster assumption the cell's controllers |
| 161 | + rely on to run at all. |
| 162 | +- **Install the quota API locally on edge cells.** Rejected: an edge cell should not |
| 163 | + host the authoritative quota ledger. The ledger lives back in the platform; the edge |
| 164 | + reads it, it doesn't own it. |
| 165 | + |
| 166 | +--- |
| 167 | + |
| 168 | +## RBAC dependency |
| 169 | + |
| 170 | +Every topology that writes claims to a ledger it does not host depends on permissions |
| 171 | +there — but only for the operations it actually performs. None can be granted from this |
| 172 | +repository; all are platform-side prerequisites. |
| 173 | + |
| 174 | +- **Edge cell.** The cell creates, reads, and deletes claims in the remote per-project |
| 175 | + control planes — it writes each claim and polls its grant on every backoff reconcile. |
| 176 | + It needs those create/read/delete grants, but because it runs **no live watch** it |
| 177 | + does **not** need list/watch there, and it needs no project-discovery permission. |
| 178 | +- **Flat single-tenant.** The manager needs access to the co-located quota API on its |
| 179 | + own cluster. |
| 180 | +- **Management plane.** The pre-existing provider-cluster watch on project control |
| 181 | + planes is unchanged (shipped). |
| 182 | + |
| 183 | +If an edge cell lacks the create/read access it needs, claim creation and grant-polling |
| 184 | +fail: the reconcile errors and requeues, and the Instance stays gated until access is |
| 185 | +fixed. That is a loud, self-retrying failure — not a silent bypass. |
| 186 | + |
| 187 | +--- |
| 188 | + |
| 189 | +## Known limitation: cell-wide unique Instance names |
| 190 | + |
| 191 | +**Deferred — an operator constraint, not a rollout blocker.** In a flat single-tenant |
| 192 | +deployment all of a project's quota claims share one space, and a claim's identity is |
| 193 | +derived from its Instance's name. Two Instances with the **same name in different |
| 194 | +namespaces** therefore collide on a single claim: one can overwrite or block the other, |
| 195 | +so quota is attributed to the wrong Instance or a legitimate Instance is wrongly gated. |
| 196 | +This cannot happen in the management-plane or edge topologies, where claims are separated |
| 197 | +per project control plane and per project namespace. |
| 198 | + |
| 199 | +Until claim identity is qualified by namespace, the operator rule for flat single-tenant |
| 200 | +mode is: **Instance names must be unique across the whole cell**, not merely within a |
| 201 | +namespace. Qualifying claim identity by namespace is the fix, tracked separately. |
| 202 | + |
| 203 | +--- |
| 204 | + |
| 205 | +## Rollout |
| 206 | + |
| 207 | +There is no capability flag to manage; the proposed changes take effect by deployment |
| 208 | +shape: |
| 209 | + |
| 210 | +1. **Edge cells stop crashing immediately.** The live watch is simply not registered |
| 211 | + where the local cluster doesn't serve the quota API, and enforcement rides the |
| 212 | + backoff floor. Confirm the cell's platform-side create/read access to the remote |
| 213 | + ledger so that the claims it writes are actually evaluated. |
| 214 | +2. **Flat single-tenant** activates when the quota API is present locally and a project |
| 215 | + is configured. |
| 216 | +3. **Management plane** is unchanged. |
| 217 | + |
| 218 | +--- |
| 219 | + |
| 220 | +## Risks |
| 221 | + |
| 222 | +- **Edge grant latency.** With no live edge watch, a grant is observed on the backoff |
| 223 | + schedule (seconds, backing off), not instantly. Accepted as the tradeoff of deferring |
| 224 | + the edge watch; revisit if latency ever matters to the product. |
| 225 | +- **Edge access to the remote ledger.** If a cell lacks create/read on the remote |
| 226 | + per-project claims, its Instances stay gated until access is fixed — correct (no |
| 227 | + bypass), but it blocks scheduling and surfaces as recurring reconcile errors rather |
| 228 | + than a single obvious failure. |
| 229 | +- **Flat single-tenant name collision.** Same-named Instances in different namespaces |
| 230 | + contend for one claim; see [Known limitation](#known-limitation-cell-wide-unique-instance-names). |
| 231 | + |
| 232 | +--- |
| 233 | + |
| 234 | +## Open questions |
| 235 | + |
| 236 | +1. Does the co-located quota evaluator actually grant claims attributed to the configured |
| 237 | + flat-tenant project (is that project entitled), or do they stall? Platform-side |
| 238 | + behavior, verifiable only in a running deployment. |
| 239 | +2. Is the per-project create/read access a cell needs actually in place across the |
| 240 | + projects it serves? A rollout verification, not a design question. |
| 241 | +3. If edge grant latency ever matters, is a live per-project edge watch worth building? |
| 242 | + (The revisit trigger for the deferred alternative above.) |
| 243 | + |
| 244 | +--- |
| 245 | + |
| 246 | +## Out of Scope |
| 247 | + |
| 248 | +**WorkloadDeployment quota.** This design covers Instance-level quota claims only. |
| 249 | +Whether WorkloadDeployments should also emit claims, and at what resources, is a separate |
| 250 | +design. |
| 251 | + |
| 252 | +**Migration of mis-written claims.** Claims recorded on the wrong cluster before the |
| 253 | +shipped fix remain there; they are never evaluated and are not cleaned up by the |
| 254 | +corrected paths. Removing them is an operational task, not part of this design. |
0 commit comments