mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 17:07:59 +00:00
fix(lua): bound prewarm by queue depth, not by the ceiling
Production oscillated between ~46 MB and ~56 MB RSS with the openai pool cycling 1 -> 10..12 -> 1 states every couple of minutes, while peak_in_use never went above 2. Cause: the batch prewarm sized itself purely on the adapter's ceiling. With max_concurrent summing to 76, growStep is 8, so any two overlapping requests warmed 8 states — 6 more than anything was waiting for. A minute later the janitor correctly reclaimed the surplus, the next pair of overlapping requests warmed 8 again, and the pool churned boot/discard forever. The elasticity was working; the growth signal was simply wrong. Prewarm is now bounded by BOTH limits: the ceiling still caps the step, but the batch never exceeds p.waiting, the number of goroutines actually blocked on the pool. Overlapping-but-not-queued traffic (the common case) creates exactly the states it uses; a genuinely queued burst still ramps in one jump. TestContentionBatchPrewarms is rewritten to queue real waiters instead of relying on the ceiling to imply demand, and TestNoPrewarmWithoutWaiters pins the production shape: two overlapping requests against a 76-wide adapter must create exactly 2 states.
This commit is contained in:
@ -106,10 +106,11 @@ type adapterPool struct {
|
||||
}
|
||||
|
||||
const (
|
||||
// growDivisor turns the adapter's max concurrency into a growth step:
|
||||
// step = ceil(maxW / growDivisor), clamped to [1, growStepCap]. A source set
|
||||
// at max_concurrent=8 grows one state at a time; a 64-wide adapter warms 8
|
||||
// at once instead of paying 8 sequential boots on a traffic spike.
|
||||
// growDivisor turns the adapter's max concurrency into the growth step CAP:
|
||||
// step <= clamp(ceil(maxW / growDivisor), 1, growStepCap). The actual batch
|
||||
// is additionally bounded by how many callers are queued (see growPlanLocked),
|
||||
// so a wide adapter may ramp in big jumps but never warms states nobody is
|
||||
// waiting for.
|
||||
growDivisor = 8
|
||||
growStepCap = 8
|
||||
// growCooldown keeps a burst of misses from batching repeatedly while the
|
||||
@ -271,19 +272,34 @@ func (p *adapterPool) boot() (*worker, error) {
|
||||
|
||||
// growPlanLocked reserves capacity for a batch prewarm and returns how many
|
||||
// EXTRA states to boot in the background (the caller already reserved one for
|
||||
// itself). contended says the miss happened while every existing state was
|
||||
// already checked out, which is the real ramp signal: sequential traffic keeps
|
||||
// reusing one warm state and must never trigger a batch, while genuinely
|
||||
// concurrent traffic warms a whole step at once instead of paying one boot per
|
||||
// request all the way up the ramp. Caller holds p.mu.
|
||||
// itself).
|
||||
//
|
||||
// Two independent limits apply, and BOTH matter:
|
||||
//
|
||||
// - the adapter's max concurrency caps the step (growStepLocked), so a
|
||||
// high-concurrency adapter is allowed to ramp in bigger jumps than a
|
||||
// narrow one;
|
||||
// - the number of goroutines actually BLOCKED waiting for a state bounds it
|
||||
// to real demand.
|
||||
//
|
||||
// Sizing on the ceiling alone over-provisions badly: an adapter with
|
||||
// max_concurrent=76 has a step of 8, so two concurrent requests would warm 8
|
||||
// states, and the janitor would throw 7 of them away a minute later — boot,
|
||||
// discard, repeat, with RSS oscillating for no benefit. Prewarming only for
|
||||
// goroutines that are genuinely queued keeps the ramp cheap without the churn.
|
||||
//
|
||||
// Caller holds p.mu.
|
||||
func (p *adapterPool) growPlanLocked(contended bool) int {
|
||||
if !contended {
|
||||
if !contended || p.waiting <= 0 {
|
||||
return 0
|
||||
}
|
||||
if time.Since(p.lastGrow) < growCooldown {
|
||||
return 0
|
||||
}
|
||||
extra := p.growStepLocked() - 1
|
||||
if extra > p.waiting {
|
||||
extra = p.waiting // never warm more than the queue needs
|
||||
}
|
||||
if extra <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user