Changeset 0.32.0 (#206)

This commit is contained in:
2026-03-19 18:50:27 +00:00
parent 6668e546fe
commit b1266b0d7c
283 changed files with 2187 additions and 1055 deletions

View File

@@ -30,7 +30,7 @@ v0.9.xv0.28.7 Foundation through Platform Polish ✅
│ │
Extension Track Operations Track
│ │
v0.29.0 Starlark Sandbox ✅ v0.32.0 Multi-Replica HA
v0.29.0 Starlark Sandbox ✅ v0.32.0 Multi-Replica HA
v0.29.1 API Extensions ✅ v0.33.0 Observability
v0.29.2 DB Extensions ✅ v0.34.0 Data Portability
v0.29.3 Workflow Forms ✅ │
@@ -39,7 +39,7 @@ v0.9.xv0.28.7 Foundation through Platform Polish ✅
v0.30.2 Workflow Packages ✅ │
v0.31.0 Editor + SDK ✅ │
v0.31.1 SDK Exercise ✅ │
v0.31.2 Team Workflows │
v0.31.2 Team Workflows
│ │
│ v0.35.0 Workflow Product
│ │
@@ -350,43 +350,39 @@ Depends on: v0.31.1.
Parallel to extension track. No Starlark dependency. Delivers
production readiness for the target multi-team deployment.
### v0.32.0 — Multi-Replica HA
### v0.32.0 — Multi-Replica HA
Run 23 backend replicas across nodes for node-level availability.
Depends on: v0.28.8.
**Design decision:** PG `SKIP LOCKED` replaces Kubernetes Lease-based
leader election. Every replica runs the scheduler poll loop; PG
serializes task claims atomically. No K8s API dependency, no Redis,
no new coordination infrastructure. See `docs/DESIGN-0.32.0.md`.
**What already works multi-replica:**
- REST API (stateless, JWT auth) ✅
- PG + S3 + CephFS (shared infrastructure) ✅
- `pg_broadcast` LISTEN/NOTIFY (cross-pod event bus) ✅
**What needs work:**
- [ ] WebSocket fan-out: wire `Bus.Subscribe` to `pg_broadcast` listener
so events from other pods reach local WebSocket connections.
LISTEN/NOTIFY plumbing exists — bridge inbound NOTIFY into per-pod
hub for local delivery.
- [ ] Task scheduler leader election: Kubernetes `Lease`-based. Only
leader runs cron scheduler. Prevents duplicate execution.
- [ ] Shared ticket store: `TicketStore` from `sync.Map` → PG table
with 30s TTL. Ensures WS ticket from pod-1 validates on pod-2.
- [ ] Shared rate limiter: in-memory → PG or Redis counter. Without
this, effective rate limit = N × configured across N replicas.
- [ ] Health check refinement: readiness probe fails fast on PG
connection loss.
- [ ] Helm: `backend.replicaCount` > 1 tested. Pod anti-affinity
to spread across nodes.
**Delivered (5 changesets):**
- [x] Task scheduler: `FOR UPDATE SKIP LOCKED` atomic claim replaces
`ListDue`. `CreateRunExclusive` conditional insert for
belt-and-suspenders uniqueness. Startup jitter (015s).
- [x] WebSocket cross-pod delivery: `PublishToUser` routes through
bus → `pg_broadcast` → remote pod. 18 `SendToUser` call sites
migrated. `tool.result.*` routing changed to `DirBoth` for
cross-pod `WaitFor`. `TargetUserID` field on `Event`.
- [x] Shared ticket store: `ws_tickets` PG table with 30s TTL,
atomic `DELETE ... RETURNING` validation. Replaces `sync.Map`.
- [x] Shared rate limiter: `rate_limit_counters` PG table with
fixed-window counters. Fail-open policy on DB errors.
- [x] Health probes: `/healthz/ready` (PG ping, 2s timeout),
`/healthz/live` (process alive). Helm: `replicaCount: 2`,
pod anti-affinity, readiness/liveness probes.
**Sizing (measured on v0.28.8):**
| Metric | Value |
|--------|-------|
| Idle memory | 17Mi |
| Peak (569 reqs, 4 users, SSE) | 216Mi |
| Settled after GC | 34Mi |
| Per-SSE stream | ~500KB1MB |
| Per-WebSocket | ~64KB |
| DB pool | 25 max (shared) |
**Schema:** `020_ha.sql` — `ws_tickets`, `rate_limit_counters`.
### v0.33.0 — Observability