|
| 1 | +--- |
| 2 | +status: provisional |
| 3 | +stage: alpha |
| 4 | +latest-milestone: "v0.x" |
| 5 | +--- |
| 6 | + |
| 7 | +<!-- |
| 8 | +TODO (datum-cloud/enhancements process — not yet done): |
| 9 | +- Open a tracking issue in datum-cloud/enhancements and link it here. The doc's |
| 10 | + final location (area directory + short title) is decided in that issue. |
| 11 | +- If this lands in the enhancements repo, place it under the compute area, e.g. |
| 12 | + `enhancements/compute/quota-enforcement/README.md`. |
| 13 | +- Assign the PR to sponsoring approvers before moving status -> implementable. |
| 14 | +
|
| 15 | +Status note: management-plane enforcement is already IMPLEMENTED; the edge and |
| 16 | +flat single-tenant designs are PROPOSED. Overall status is kept `provisional` |
| 17 | +until the proposed portion is approved. |
| 18 | +--> |
| 19 | + |
| 20 | +# Quota Enforcement Across Compute Deployments |
| 21 | + |
| 22 | +- [Summary](#summary) |
| 23 | +- [Motivation](#motivation) |
| 24 | + - [Goals](#goals) |
| 25 | + - [Non-Goals](#non-goals) |
| 26 | +- [Proposal](#proposal) |
| 27 | + - [User Stories](#user-stories) |
| 28 | + - [Notes/Constraints/Caveats](#notesconstraintscaveats) |
| 29 | + - [Risks and Mitigations](#risks-and-mitigations) |
| 30 | +- [Design Details](#design-details) |
| 31 | + - [Where claims live decides how grants are observed](#where-claims-live-decides-how-grants-are-observed) |
| 32 | + - [Claims route to the owning ledger](#claims-route-to-the-owning-ledger) |
| 33 | + - [Flat single-tenant identity](#flat-single-tenant-identity) |
| 34 | + - [Access prerequisites](#access-prerequisites) |
| 35 | +- [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) |
| 36 | + - [Feature Enablement and Rollback](#feature-enablement-and-rollback) |
| 37 | + - [Monitoring Requirements](#monitoring-requirements) |
| 38 | + - [Dependencies](#dependencies) |
| 39 | + - [Scalability](#scalability) |
| 40 | + - [Troubleshooting](#troubleshooting) |
| 41 | +- [Implementation History](#implementation-history) |
| 42 | +- [Drawbacks](#drawbacks) |
| 43 | +- [Alternatives](#alternatives) |
| 44 | +- [Infrastructure Needed](#infrastructure-needed) |
| 45 | + |
| 46 | +## Summary |
| 47 | + |
| 48 | +Customers should be able to run quota-enforced Instances on **any** Datum deployment — |
| 49 | +the central management plane, an edge cell, or a flat single-tenant cluster. Today they |
| 50 | +can't: enabling quota anywhere but the management plane either silently never evaluates |
| 51 | +(claims recorded where the quota system never reads them) or crashes compute-manager |
| 52 | +outright. This enhancement makes enforcement work in all three deployments, so an |
| 53 | +Instance is gated on a real quota grant wherever it runs. The single organizing idea is |
| 54 | +that a quota claim is created in — and read from — the control plane that owns the |
| 55 | +project's quota ledger, and how a grant is observed follows from where that ledger sits. |
| 56 | + |
| 57 | +## Motivation |
| 58 | + |
| 59 | +Quota is how the platform bounds what customers can consume. If it only works in the |
| 60 | +central management plane, then every deployment closer to the customer — edge cells and |
| 61 | +single-tenant clusters — either runs ungated or can't run compute-manager at all. The |
| 62 | +crash on edge cells is a hard blocker to standing compute up there. Making enforcement |
| 63 | +portable across deployments is what lets quota-bearing products ship beyond the |
| 64 | +management plane. |
| 65 | + |
| 66 | +### Goals |
| 67 | + |
| 68 | +- Enforce quota on Instances in all three deployments: management plane, edge cell, flat |
| 69 | + single-tenant. |
| 70 | +- compute-manager starts and stays healthy with quota enabled in every deployment (no |
| 71 | + startup crash). |
| 72 | +- An Instance is gated until its quota is actually granted, and ungated promptly once it |
| 73 | + is. |
| 74 | + |
| 75 | +### Non-Goals |
| 76 | + |
| 77 | +- WorkloadDeployment-level quota (this covers Instance-level claims only). |
| 78 | +- A live claim watch at the edge (deferred — see [Alternatives](#alternatives)). |
| 79 | +- Cleanup or migration of claims written to the wrong cluster before the shipped fix. |
| 80 | +- Changes to the quota evaluator itself; this enhancement produces claims and observes |
| 81 | + grants, it does not decide them. |
| 82 | + |
| 83 | +## Proposal |
| 84 | + |
| 85 | +The deployments differ in exactly one way that matters: **where an Instance's |
| 86 | +ResourceClaim lives**, and from that, how a grant is observed. |
| 87 | + |
| 88 | +| Deployment | Where ResourceClaims live | How quota is enforced | |
| 89 | +|---|---|---| |
| 90 | +| **Management plane** | Per-project Milo control planes the manager already engages | Live watch on those claims (shipped) | |
| 91 | +| **Edge cell** | Remote Milo — one control plane per project, not on the cell | Claim written to the owning project; grant observed via the always-on backoff reconcile — no live watch | |
| 92 | +| **Flat single-tenant** | Co-located on the one cluster | Live watch on the local claims | |
| 93 | + |
| 94 | +In every case a claim is created in, and read from, the control plane that owns the |
| 95 | +project's quota ledger — write and read must be the same ledger, or grants are never |
| 96 | +seen. Enforcement has an always-on backoff floor (a pending Instance's reconcile re-runs |
| 97 | +on a backing-off schedule and re-checks its claim); at the edge that floor is the sole |
| 98 | +liveness mechanism, and everywhere else it backstops the live watch. |
| 99 | + |
| 100 | +### User Stories |
| 101 | + |
| 102 | +- As an operator, I stand up compute-manager on an edge cell with quota enabled; it |
| 103 | + starts cleanly, and customer Instances there are gated on grants from their projects' |
| 104 | + quota ledgers back in the platform. |
| 105 | +- As an operator, I run a flat single-tenant cluster with quota co-located; Instances are |
| 106 | + gated on grants from the local ledger, with the cluster's single project named in |
| 107 | + configuration. |
| 108 | + |
| 109 | +### Notes/Constraints/Caveats |
| 110 | + |
| 111 | +- **Flat single-tenant identity.** A flat deployment may not run the platform layer that |
| 112 | + derives a claim's project from namespace-ownership labels, so it names its single |
| 113 | + project explicitly in configuration and attributes every claim to it. |
| 114 | +- **Cell-wide Instance name uniqueness (flat mode).** See the known limitation under |
| 115 | + [Drawbacks](#drawbacks) — in flat mode Instance names must be unique across the whole |
| 116 | + cell. |
| 117 | + |
| 118 | +### Risks and Mitigations |
| 119 | + |
| 120 | +- **Edge grant latency.** With no live edge watch, a grant is observed on the backoff |
| 121 | + schedule (seconds, backing off), not instantly. Accepted as the tradeoff of deferring |
| 122 | + the edge watch; correctness is unaffected (an Instance stays gated until granted). |
| 123 | +- **Missing edge access to the remote ledger.** If a cell can't reach its projects' |
| 124 | + ledgers, its Instances stay gated until access is fixed — correct (never a bypass), but |
| 125 | + it blocks scheduling and shows up as recurring reconcile errors rather than one obvious |
| 126 | + failure. |
| 127 | +- **Flat-mode name collision.** Same-named Instances in different namespaces contend for |
| 128 | + one claim; mitigated by the cell-wide-uniqueness rule until claim identity is |
| 129 | + namespace-qualified. |
| 130 | + |
| 131 | +## Design Details |
| 132 | + |
| 133 | +### Where claims live decides how grants are observed |
| 134 | + |
| 135 | +A **live** claim watch runs only where the claims sit on a cluster the manager already |
| 136 | +engages — the management plane's per-project control planes, and a flat single-tenant |
| 137 | +cluster's local API. An edge cell's claims are remote and split per project, so it runs |
| 138 | +no live watch. That also removes the startup crash: the edge's local cluster does not |
| 139 | +serve the quota API and should never have been asked to watch that kind. The rule is |
| 140 | +simply: watch claims live only where a cluster the manager already engages serves the |
| 141 | +quota API; determining this must fail loud rather than silently resolve to "off." |
| 142 | + |
| 143 | +### Claims route to the owning ledger |
| 144 | + |
| 145 | +Creating, reading, and deleting an Instance's claim targets the control plane that owns |
| 146 | +the project's quota ledger — the project control plane for management plane and edge, the |
| 147 | +local cluster for flat single-tenant. At the edge this routing doubles as the liveness |
| 148 | +mechanism: each backoff reconcile creates-or-reads the remote claim and checks whether it |
| 149 | +has been granted. |
| 150 | + |
| 151 | +### Flat single-tenant identity |
| 152 | + |
| 153 | +Because a flat deployment may lack the platform namespace-ownership layer, it takes its |
| 154 | +project identity from configuration and attributes every claim to that one project. If |
| 155 | +local quota is enabled without a configured project, the deployment refuses to start |
| 156 | +rather than emit claims the evaluator can't attribute. |
| 157 | + |
| 158 | +### Access prerequisites |
| 159 | + |
| 160 | +Each deployment needs only the access for the operations it performs, granted |
| 161 | +platform-side (not from this repository): |
| 162 | + |
| 163 | +- **Edge cell:** create/read/delete on the remote per-project claims — but no list/watch, |
| 164 | + since it runs no live watch, and no project-discovery permission. |
| 165 | +- **Flat single-tenant:** access to the co-located quota API on its own cluster. |
| 166 | +- **Management plane:** the existing provider-cluster watch is unchanged. |
| 167 | + |
| 168 | +## Production Readiness Review Questionnaire |
| 169 | + |
| 170 | +<!-- Trimmed to the subsections relevant at alpha, following the precedent of other |
| 171 | +compute enhancements. Beta-targeted subsections (rollout/upgrade/rollback planning) are |
| 172 | +deferred. --> |
| 173 | + |
| 174 | +### Feature Enablement and Rollback |
| 175 | + |
| 176 | +- [ ] Feature gate |
| 177 | +- [x] Other |
| 178 | + - Enablement is by deployment configuration, not a global gate: a deployment enforces |
| 179 | + quota when it is configured with access to a quota ledger (and, for flat |
| 180 | + single-tenant, a configured project). No configuration means quota is not enforced. |
| 181 | + - Enabling/disabling requires no control-plane downtime; it takes effect on |
| 182 | + compute-manager restart with the changed configuration. |
| 183 | + - Rollback is removing the quota configuration; enforcement stops and Instances are no |
| 184 | + longer gated on grants. |
| 185 | + |
| 186 | +### Monitoring Requirements |
| 187 | + |
| 188 | +- [x] API .status |
| 189 | + - The Instance's quota-granted status condition tells an operator whether an Instance is |
| 190 | + gated or granted. An Instance stuck ungated indicates quota is pending or a ledger it |
| 191 | + can't reach. |
| 192 | + |
| 193 | +### Dependencies |
| 194 | + |
| 195 | +- Milo quota evaluator and the per-project (or local) quota ledgers it serves — the |
| 196 | + authority that grants or denies claims. If unavailable, claims are not granted and |
| 197 | + Instances stay gated (no bypass). |
| 198 | +- Platform-side access grants (see [Access prerequisites](#access-prerequisites)). |
| 199 | + |
| 200 | +**Open verification item.** It is not yet confirmed that Milo's quota evaluator actually |
| 201 | +grants a flat single-tenant claim attributed to the configured project — i.e. that the |
| 202 | +project is entitled in a co-located ledger. This is platform-side behavior, testable only |
| 203 | +in a running deployment, and must be verified before relying on flat single-tenant |
| 204 | +enforcement. |
| 205 | + |
| 206 | +### Scalability |
| 207 | + |
| 208 | +- The edge path adds no live watch, so it introduces no per-project watch fan-out; its |
| 209 | + only added calls are the per-Instance claim create/read on the backoff schedule. |
| 210 | + |
| 211 | +### Troubleshooting |
| 212 | + |
| 213 | +- **Instances stuck ungated on an edge cell:** confirm the cell's create/read access to |
| 214 | + its projects' remote ledgers, and that those projects are entitled so claims are |
| 215 | + granted rather than left pending. |
| 216 | +- **Instances stuck ungated in flat single-tenant:** confirm a project is configured and |
| 217 | + that the local evaluator grants claims attributed to it (see the open verification item |
| 218 | + under [Dependencies](#dependencies)). |
| 219 | + |
| 220 | +## Implementation History |
| 221 | + |
| 222 | +- Management-plane claim routing and the live watch on project control planes: |
| 223 | + **implemented** (verified 2026-05-26). |
| 224 | +- Edge cell (no live watch, backoff-only) and flat single-tenant (co-located watch, |
| 225 | + configured project): **proposed** here; edge fixes the startup crash. |
| 226 | + |
| 227 | +## Drawbacks |
| 228 | + |
| 229 | +- Edge enforcement observes grants on the backoff schedule, not instantly — a latency |
| 230 | + cost relative to a live watch. |
| 231 | +- Flat single-tenant carries an operator constraint until claim identity is |
| 232 | + namespace-qualified: **Instance names must be unique across the whole cell**, because a |
| 233 | + claim's identity derives from its Instance's name and all of a project's claims share |
| 234 | + one space, so two same-named Instances in different namespaces would collide on one |
| 235 | + claim. |
| 236 | + |
| 237 | +## Alternatives |
| 238 | + |
| 239 | +- **A live edge watch via a per-project-control-plane forwarder** — *deferred*. It means |
| 240 | + one watch per project control plane a cell serves (fan-out that grows with projects), |
| 241 | + and the always-on backoff already delivers grants within seconds, so the machinery |
| 242 | + isn't worth it now. Revisit if edge grant latency ever becomes a product concern. |
| 243 | +- **A single unified multi-cluster provider spanning the cell and the remote ledger** — |
| 244 | + rejected; it destabilizes the cell's control path by breaking the single-cluster |
| 245 | + assumption its controllers rely on. |
| 246 | +- **Installing the quota API locally on edge cells** — rejected; the edge should read the |
| 247 | + authoritative ledger, not host it. |
| 248 | + |
| 249 | +## Infrastructure Needed |
| 250 | + |
| 251 | +- Platform-side access grants in the project (or local) control planes, per |
| 252 | + [Access prerequisites](#access-prerequisites). These cannot be granted from the compute |
| 253 | + repository and should be tracked via the enhancement's tracking issue. |
0 commit comments