# 9. Withdrawn: cp9 pre-warms one shared dataplane pool per env; creates reuse it

Status: Withdrawn

Withdrawn on 2026-07-06 as part of the command-surface cleanup: `cp9 prewarm`
and `cp9 prewarm status` were removed, and cp9 no longer has an accepted
warm-pool prewarm command/status surface. This ADR remains as historical design
context only.

## Context

A Premium `create → ACTIVE` takes ~33–35 min, of which ~18 min is provisioning the
dataplane EKS (control plane + node groups + network/appbox). For agent auto-loops — cp9's
primary use — that cold provision is paid on every create and is the dominant blocker.

The create is already two phases: `create-sharedpool` (provision the dataplane pool) and
`create-tidbcluster` (place a logical cluster / `TiDBGroup` on the pool by `SharedPoolId`).
Today the pool is created lazily on the first create (`AGENTS.md`: "DPs are create/delete
dynamically regarding API call. Not provisioned in CD stage."). Shared pools already persist
across cluster delete (`enableCleanSharedPool: false`), so a pool, once created, is reused.

Constraints: the dataplane must stay a **real EKS** (customer data plane — fidelity); the
budget is tight, so **no extra always-on EKS** beyond the env's own; and the change must be
**cp9-internal** (no upstream business-repo changes).

## Decision

cp9 pre-warms the environment's **single** shared dataplane pool, so subsequent creates skip
the ~18 min provision. The shape, settled by grilling:

- **Async pre-warm (Q1).** The pre-warm runs at env-up but does **not** block `cp9 up` on the
  pool reaching ACTIVE — it provisions in the background. cp9 **must expose a poll/status
  capability** for "is the warm pool ready" so a caller can check before relying on it
  (reuse the existing dataplane/pool status, or a `cp9 status` surface).
- **Pool-only provision via the existing OPS API.** cp9 provisions just the pool (~18 min) by
  calling the **`create-sharedpool` gRPC OPS API on tidb-mgmt-service** as a client — no
  keeper cluster needed, and no upstream source change.
- **Minimal pool topology.** The pre-warm uses the smallest/fewest TiDB components and EC2
  instances the pool allows, to shorten pool creation. Sized via the cluster-ng meta-DB
  config (cp9 owns the seed) and tunable `create-sharedpool` API parameters.
- **Nodes scale-to-zero (Q2).** The warm idle pool is just the EKS control plane + network +
  appbox — **no idle worker nodes**. The first create on an empty pool pays the ~7 min
  node-join once per env; later creates land while nodes are already up.
- **No keeper (Q3).** Rely on pool persistence. Verified there is no idle-GC that reaps the
  empty premium pool: `enableCleanSharedPool: false` (the cluster-ng pool cleaner is off),
  the premium dataplane-manager has no idle/GC config, and ADR-adjacent change #624 makes the
  dedicated regional-server ignore `servicetype=premium`, so its `idleTimeThreshold: 24h` GC
  does not apply to this pool. The pool lives for the env's lifetime.
- **Teardown is already handled by existing tagging.** The pool must be reaped by
  `cp9 destroy` (otherwise a pre-warmed pool leaks a real EKS). The pool is provisioned **by the
  dataplane-manager**, which cp9 already patches at env-up to stamp `cp9-owner`/`cp9-session`
  (and `cp9-ttl`/`cp9-account`/`cp9-region`) onto every Crossplane-created AWS resource via its
  **`additionalCloudTags`** (see `internal/lifecycle/tiltfile_generate_step.go`
  `patchLocalDataplaneCloudTagsRootConfig`). So a pre-warmed pool inherits the tag invariant for
  free and the existing tag-scoped destroy reaps it — no new tag plumbing is needed. (This is
  the working mechanism in-repo; it is *not* a Crossplane ProviderConfig `defaultTags`, which
  this repo deliberately does not use. Replicate the `additionalCloudTags`/`forProvider.tags`
  stamping only if a pool resource is ever created on a path that bypasses the dataplane-manager.)
- **Reuse is the existing product mechanism.** A Premium create lands a logical cluster on an
  existing matching shared pool by `SharedPoolId`; cp9 only has to ensure the pool exists
  before the first create. No cluster-ng / dataplane-manager source changes.

## Consequences

- `create → ACTIVE` on a pre-warmed env drops from ~33–35 min to roughly the component
  rollout (~11 min, less with pre-pulled images / smaller dev topology), plus a one-time
  ~7 min node-join on the env's first create.
- **Cost:** one EKS control plane (~$0.10/hr) for the env's lifetime, **no idle nodes** — the
  same single EKS the env would create on its first cluster anyway, just earlier and
  persisted. No extra always-on EKS.
- **Fidelity:** the dataplane is a real EKS, unchanged.
- **Scope:** cp9-internal — a lifecycle pre-warm step plus a warm-pool-readiness status/poll
  surface. No upstream business-repo changes.
- **New surface required:** the readiness poll (async pre-warm makes it necessary).
- **Risk to watch:** if a future change introduces an idle reaper for premium pools, the
  no-keeper assumption breaks and a minimal keep-alive would be needed.
