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

@@ -1,5 +1,70 @@
# Changelog
## [0.32.0] — 2026-03-19
### Summary
Multi-Replica HA: run 2+ backend replicas across nodes for node-level
availability. All shared mutable state moves from in-process memory to
Postgres — task scheduler uses `FOR UPDATE SKIP LOCKED` for atomic
claim (no leader election), WebSocket tickets and rate limit counters
become PG tables, and `SendToUser` is replaced by bus-routed
`PublishToUser` for cross-pod event delivery. New `/healthz/ready`
and `/healthz/live` probe endpoints. Helm defaults to 2 replicas with
pod anti-affinity.
### New
- **`FOR UPDATE SKIP LOCKED` task scheduler** — every replica polls;
PG serializes task handoff atomically. `ClaimDueTask` replaces
`ListDue`. `CreateRunExclusive` conditional insert prevents
double-execution. Startup jitter (015s) staggers replicas.
- **PG ticket store** — `ws_tickets` table replaces in-memory
`sync.Map`. Atomic `DELETE ... RETURNING` for single-use validation.
30s TTL, reaper piggybacked on scheduler poll tick.
- **PG rate limiter** — `rate_limit_counters` table with fixed-window
`INSERT ON CONFLICT DO UPDATE`. Fail-open policy: DB errors allow
the request rather than blocking auth. Cleanup piggybacked on
scheduler poll tick.
- **Cross-pod WebSocket delivery** — `Hub.PublishToUser` routes events
through the bus → `pg_broadcast` → remote pod `publishLocal`.
`TargetUserID` field on `Event` for targeted filtering.
`tool.result.*` routing changed to `DirBoth` so browser tool results
cross pods for `WaitFor`.
- **Health probes** — `/healthz/ready` (PG ping, 2s timeout) and
`/healthz/live` (process alive, no deps).
- **Helm HA** — `backend.replicaCount: 2`, pod anti-affinity
(`preferredDuringScheduling`, `kubernetes.io/hostname`), readiness
and liveness probes pointed at new healthz endpoints.
- **Schema `020_ha.sql`** — `ws_tickets` and `rate_limit_counters`
tables (PG + SQLite).
### Changed
- **`SendToUser``PublishToUser`** — 18 call sites across 10 files
migrated to bus-routed delivery for cross-pod reach.
- **`ListDue` removed from `TaskStore`** — replaced by `ClaimDueTask`
(atomic claim) + `CreateRunExclusive` (belt-and-suspenders guard).
- **Rate limiter middleware** — rewritten to accept `store.RateLimitStore`
instead of managing in-memory state. Constructor signature changed:
`NewRateLimiter(store, rate, burst)`.
- **`tool.result.*` routing** — `DirFromClient``DirBoth` to enable
cross-pod `WaitFor`. WS subscriber explicitly filters `tool.result`
to prevent re-sending to clients.
### Removed
- **`events/tickets.go`** — in-memory `TicketStore` replaced by
`store.TicketStore` (PG/SQLite) + `TicketValidatorAdapter`.
- **In-memory rate limiter** — visitor map and cleanup goroutine
replaced by PG-backed store.
### Fixed
- **Team workflow route registration** — v0.31.2 team-scoped workflow
CRUD routes (`/teams/:teamId/workflows`) were missing from `main.go`
route registration. 12 routes added to `teamScoped` group.
## [0.31.2] — 2026-03-19
### Summary