Skip to content

Add member_order (fifo/lifo) + demand-history cull#110

Open
seriyps wants to merge 1 commit into
epgsql:masterfrom
seriyps:member-order
Open

Add member_order (fifo/lifo) + demand-history cull#110
seriyps wants to merge 1 commit into
epgsql:masterfrom
seriyps:member-order

Conversation

@seriyps
Copy link
Copy Markdown
Member

@seriyps seriyps commented May 29, 2026

  • Add member_order => lifo | fifo pool config (lifo is the default and preserves
    all existing behaviour). fifo uses an OTP queue as the free-member structure,
    handing out the longest-idle worker first (round-robin rotation).

  • Replace the per-worker max_age idle-time cull gate with pool-level demand
    history
    : a fixed-capacity queue ring buffer (head = current bucket) tracks peak
    concurrent in-use count over a rolling max_age window. At each cull tick the pool
    sizes to that peak (floored at init_count). This gives LIFO and FIFO identical
    shrinkage behaviour for the same config — FIFO workers never go idle under rotation,
    so the old per-worker check never fired for them.

  • max_age now means "demand memory window" rather than "individual worker idle time".
    Practical behaviour for LIFO pools is essentially unchanged; FIFO pools now cull
    correctly. cull_interval default changed from {1, min} to {15, sec} so the
    defaults satisfy cull_interval ≤ max_age.

  • Fixed-size pools (init_count == max_count) skip demand tracking entirely
    (demand_buf = undefined) — cull never fires for them anyway.

  • Hot upgrade v5 → v6: demand_buf is reconstructed from member time timestamps.

Benchmark (OTP 27.3, vs master baseline)

LIFO path overhead is within noise (0–5%) for all existing benchmarks; the cull-heavy
benchmark improves by ~8%. FIFO adds ~10–15% vs LIFO for single take/return
(queue vs list-head), negligible for drain-all and concurrent patterns.

Introduces two related changes:

1. `member_order => fifo | lifo` pool config option (default `lifo`,
   preserving existing stack behaviour). `fifo` uses an OTP `queue()`
   as the free-member structure, producing round-robin dispatch where
   the longest-idle worker is always handed out next.  The free-member
   abstraction (`free_push/pop/delete/fold/take_n/convert`) hides the
   list-vs-queue difference from all call sites; `free_count` is now
   exclusively managed by these helpers.

2. Pool-level demand-history cull replaces the per-worker `max_age`
   idle-time check.  A fixed-capacity OTP-queue ring buffer (head =
   current bucket, capacity = ceil(max_age/bucket_size)) tracks peak
   concurrent in-use count; at each cull tick the pool sizes to that
   peak (floored at `init_count`).  This gives LIFO and FIFO pools
   identical shrinkage behaviour for the same config, and correctly
   handles FIFO rotation where workers never accumulate idle time.

   `max_age` now means "demand memory window"; `cull_interval` default
   changed from {1,min} to {15,sec} so defaults satisfy CI <= MA.
   Hot-upgrade (vsn 5->6) reconstructs the demand buffer from member
   `time` timestamps.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant