Skip to content

AccountPool module: state machine, round-robin, inflight cap, queue timeout #3

Closed

Description

@longphamdev

AccountPool module (thuần, chưa nối handler)

What to build

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)
  • Không import gì từ server/handler (module thuần)

Activity

  1. longphamdev commented on Nov 9, 2026

    @longphamdev
    OwnerAuthor

    Implementation notes (từ review #1 — ràng buộc cứng):

    • 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.

  2. longphamdev commented on Nov 9, 2026

    @longphamdev
    OwnerAuthor

    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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions