You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
AccountPool module: state machine, round-robin, inflight cap, queue timeout #3
Module AccountPool riêng biệt — quản lý vòng đời runtime của các account, unit test được mà không đụng handler:
Runtime state (RAM, không persist): healthy / cooling (retryAt) / exhausted (quotaExpiresAt) / expired, per account. Load từ store lúc khởi tạo; runtime state KHÔNG ghi vào credentials.json.
Pick strategy: mặc định round-robin + skip unhealthy + cap inFlight per account (config). Option config: most-remaining-quota (khi poller có data — ticket 4).
Reserve/release: idempotent (release 2 lần không giảm inFlight dưới 0), thread-safe qua Promise serialization.
Queue: khi tất cả acc đạt cap → hàng đợi ngắn có timeout (accounts.queueTimeoutMs, default 5s, wake khi release() — không poll interval); quá hạn → 429 + Retry-After: 1.
All-cooling → 429 + Retry-After = min retryAt các acc cooling. Toàn exhausted/expired → lỗi 503-style message rõ ràng, KHÔNG kèm Retry-After nhiều ngày.
Hot-reload sync: store file đổi (mtime+size) → pool nạp account mới/xóa acc mất tích, giữ nguyên runtime state của acc còn lại.
Sync không ghi đè: pool refresh credential từ store nhưng status/inFlight giữ nguyên.
Acceptance criteria
Round-robin phân đều qua N acc healthy
Acc cooling/exhausted/expired bị skip
reserve/release idempotent; inFlight không âm; không leak qua release 2 lần
Tất cả đạt cap → queue, wake khi release, timeout → lỗi đúng shape
All-cooling → 429 + Retry-After = min retryAt; all-exhausted → 503 không Retry-After
Store thêm acc lúc pool đang chạy → acc mới pick được; xóa acc → không pick nữa; runtime state cũ giữ nguyên
State machine transitions có bảng test đầy đủ (state × event → next state)
Injectable clock: mọi thứ dựa trên thời gian (cooling retryAt, queue timeout, exhausted tới hạn) phải đọc từ clock inject được — test không sleep thật.
release() idempotent: gọi 2 lần không làm inFlight âm, không double-wake queue.
Queue wake bởi release(): không poll theo interval; promise pending được resolve khi slot trống.
Cap inFlight mặc định đủ rộng cho stream dài (Claude Code giữ slot hàng phút): default đề xuất 4+ per acc, có config accounts.maxInFlightPerAccount.
Test đồng thời: N request cùng lúc (vd 20) qua 3 acc healthy → phân phối không dồn vào 1 acc (assert mỗi acc nhận >0 và chênh lệch hợp lý); hết cap → queue hoạt động đúng.
Phạm vi: module thuần, không import server/handler. Unit test với store seeded trực tiếp.
Activity
longphamdev commentedon Nov 9, 2026
Implementation notes (từ review #1 — ràng buộc cứng):
release()idempotent: gọi 2 lần không làminFlightâm, không double-wake queue.release(): không poll theo interval; promise pending được resolve khi slot trống.inFlightmặc định đủ rộng cho stream dài (Claude Code giữ slot hàng phút): default đề xuất 4+ per acc, có configaccounts.maxInFlightPerAccount.Phạm vi: module thuần, không import server/handler. Unit test với store seeded trực tiếp.
longphamdev commentedon Nov 9, 2026
AccountPool hoàn thành: module thuần (injectable clock, không import handler), state machine đầy đủ, reserve/release idempotent, queue wake-by-release FIFO + timeout, lazy revival cho cooling/exhausted, syncWithStore giữ runtime state của acc còn lại.
23 tests mới. Toàn suite: 1172 pass / 2 pre-existing abort failures. Error shapes cho handler map sẵn: AllAccountsCoolingError (429+Retry-After=min retryAt), NoAccountAvailableError (503), AccountsBusyError (429, Retry-After 1s).
Frontier: #4 (handler integration — có bước đầu điều tra 2 abort tests fail pre-existing).
feat(auth): multi-account store v2 + Phase 1 hardening (#1)
feat(auth): AccountPool module — runtime state machine, round-robin, …