v1 plan: fix spread test config, queue test read, admin pin via lease.Candidates; note the findings

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 04:29:16 -07:00
co-authored by Claude Fable 5.1
parent 29b3a5c38e
commit 1cc645a85b
3 changed files with 61 additions and 8 deletions
+14 -2
View File
@@ -18,6 +18,15 @@ builds the lease table and limiter, runs idle expiry and pruning, and shuts down
## Interfaces
`internal/lease` gains one method — the only change to that package allowed in this task:
```go
// Candidates records hosts as seen for route (idempotent), so Pin can accept a host the route
// is configured for before any request has used it. cmd/crossbar calls it for every route at
// start; the admin handler calls it before Pin.
func (t *Table) Candidates(route string, hosts []string)
```
`internal/admin`, package `admin`:
```go
@@ -48,7 +57,9 @@ Endpoints (all JSON unless said; errors `{"error":"…"}`; wrong method → 405
- `GET /_crossbar/hosts` → `map[string]HostView`.
- `GET /_crossbar/routes` → `map[string]RouteView` from config + `lt.Snapshot()` + `lt.Pinned`.
- `POST /_crossbar/routes/{route}` body `{"host":"…","pin":true}` → `lt.Pin(route, host, now)`;
- `POST /_crossbar/routes/{route}` body `{"host":"…","pin":true}`: first check `host` is one of
`cfg.Routes[route].Hosts` (else **404**), then `lt.Candidates(route, cfg.Routes[route].Hosts)` so
the table knows them even if no request has used the route yet, then `lt.Pin(route, host, now)`;
`{"release":true}` → `lt.Release(route)` **and** `lt.Unpin(route)`. Unknown route → 404;
`lt.Pin` returning `ErrUnknownHost` → 404; not JSON, neither form, or both forms at once,
or `pin` without `host` → 400. Answer `{"ok":true}` (plus `"released": n` for a release).
@@ -88,6 +99,7 @@ Endpoints (all JSON unless said; errors `{"error":"…"}`; wrong method → 405
- After `config.Load`: `store.Open(cfg.DB)` (error → exit 1 `crossbar: …`); `defer st.Close()`.
- `hosts := proxy.HostView(table, cfg)`; `lim := limiter.New()` configured for every host/model
from config with `cfg.QueueMax`; `leases, err := lease.New(st, hosts, proxy.Chooser(cfg, table, lim), cfg.LeaseIdle.Duration)`.
- `for name, rt := range cfg.Routes { leases.Candidates(name, rt.Hosts) }`.
- `proxy.New(cfg, table, leases, lim, st, log)`; `admin.Handler(cfg, table, leases, lim, st, hosts)`.
- Background loops until ctx is done: every minute `leases.ExpireIdle(time.Now())`; every hour
`st.Prune(time.Now(), cfg.Retention.Duration)`; after every poll round the health table's
@@ -112,7 +124,7 @@ Expected: `listening`, `shutting down`, `exit=0`; the SQLite file was created (t
- [ ] **6. Run the gate.** `make gate`. **7. Log and commit.** Row `v1/06-admin-main`.
```sh
git add internal/admin internal/store cmd/crossbar example.toml docs/implementer-log.md
git add internal/admin internal/store internal/lease cmd/crossbar example.toml docs/implementer-log.md
git commit
```