v0.1.25.13 — hydration cap + enum wire annotations
v0.1.25.13 — hydration cap + enum wire annotations
Two defensive fixes on the v0.1.25.12 sorted-list feature, ported from cycles-server-admin v0.1.25.24. No spec change, no wire-format change, no hot-path performance impact.
Fixed
- P1 —
listReservationsSortedhydration cap. The sorted path no longer hydrates an unbounded reservation population before the in-memory sort.SORTED_HYDRATE_CAP = 2000mirrors the admin-plane pattern: a labeled break exits the SCAN loop oncematching.size() >= cap, a WARN is logged naming the tenant + sort tuple, and the downstream sort/slice/cursor path operates on the capped slice. Page still fills,has_more+next_cursorstill populate — the cap is a heap-safety bound, not a correctness bound. Legacy no-sort-params path intentionally uncapped (it streams page-by-page via the SCAN cursor). - P2 — Jackson wire annotations on
ReservationSortBy+SortDirection. Both enums now carry@JsonValue getWire()+@JsonCreator fromWire(String)matching the admin plane'sSortSpec/SortDirectioncontract. Wire form stays lowercase, parsing stays case-insensitive,null → null. Controller-level validation unchanged: unknown tokens still surface as HTTP 400INVALID_REQUEST.
Operator guidance
Callers that outgrow the 2000-row cap should narrow filters (status, idempotency_key, scope segments: workspace/app/workflow/agent/toolset). The longer-term path is the deferred per-tenant ZSET index ADR at docs/deferred-optimizations/sorted-list-zset-indices.md, scheduled when a tenant crosses ~10k active reservations.
Tests
RedisReservationQueryTest#sortedHydrationStopsAtCap— mocks SCAN page withcap + 10keys, asserts exactly 5 rows in ascendingcreated_at_msorder andhas_more=true,next_cursor != null.EnumsTest(new, cycles-protocol-service-model) — 12 tests covering getWire lowercase emission, fromWire canonical+case-insensitive parsing, null pass-through,IllegalArgumentExceptionon unknown tokens, round-trip identity.- Full build green: 358 (data) + 137 (api) = all tests passing; JaCoCo ≥ 95%.
Backward compatibility
Full. Behaviour-visible only for tenants whose sorted-list query previously returned >2000 matching rows — those rows beyond row 2000 in the capped slice are now unreachable without narrowing filters. Same trade-off the admin plane established in v0.1.25.24.
Docker image
ghcr.io/runcycles/cycles-server:0.1.25.13
[benchmark-skip] — no hot-path write changes. Reserve / commit / release / extend / decide / event paths byte-identical to v0.1.25.12. Sorted-list read path adds a bounds check (1 comparison per hydrated row) with no observable latency impact. Full benchmark sweep scheduled when the deferred ZSET optimization lands.