From: Frederic Lecaille Date: Mon, 24 Aug 2026 17:14:06 +0000 (+0200) Subject: MINOR: haload: keep old-scheduled users under -ee's floor rate X-Git-Tag: v3.5-dev6~79 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=dd8b6934bf3fa972c9aa4997a7f48632258df6cf;p=haproxy.git MINOR: haload: keep old-scheduled users under -ee's floor rate A user with its next send already scheduled before -ee starts still fires at the old, unlimited pace. Nothing re-paces it once the ramp begins. A mass failure could still cause a burst of new connections right when the floor kicks in. Each user now remembers the last -ee generation it checked in with. On its first wakeup during a new ramp, it checks for room under the current rate before sending. If there is none, it retries after a short, fixed delay, and checks again. A user created during the ramp already has the right generation, so replacements are not affected: -ee's doubling still only depends on their own successes. This check does not reuse hld_usr_schedule()'s wait: that one grows with the number of idle users on the thread, and right after a mass failure that number can be huge. It also never gets recomputed later, so a user could end up waiting several minutes with no way back. A short, fixed retry avoids this, since the user is checked again each time instead of trusting one old estimate. --- diff --git a/src/haload.c b/src/haload.c index 1423cc1ab..e2cd6a71d 100644 --- a/src/haload.c +++ b/src/haload.c @@ -1851,6 +1851,31 @@ static struct task *hld_usr_task(struct task *t, void *context, unsigned int sta goto skip_new_strms; } + if (HA_ATOMIC_LOAD(&err_ramp_active) && usr->ramp_gen != HA_ATOMIC_LOAD(&err_ramp_gen)) { + /* This user was scheduled before the current -ee ramp, + * under the old rate. Check for room now before it sends. + * + * Retry with a short, fixed delay if there is none, and + * check again next time. hld_usr_schedule()'s wait would + * grow with the idle user count instead, which can be huge + * right after a mass failure, giving some users a + * multi-minute wait with no way back. + * + * A user created during this ramp already has the right + * ramp_gen, so replacements are not affected. + */ + struct hld_thr_info *ti = &thrs_info[tid]; + uint32_t rate = HA_ATOMIC_LOAD(&arg_rate); + uint32_t max = hld_thr_rate_max(ti, rate); + + if (!max || !hld_freq_ctr_remain(&ti->req_rate, max, 0, date)) { + task_schedule(usr->task, tick_add(now_ms, MS_TO_TICKS(1000))); + goto skip_new_strms; + } + + usr->ramp_gen = HA_ATOMIC_LOAD(&err_ramp_gen); + } + for (url = urls; url; url = hld_next_url(urls, url)) { struct hld_path *path, *paths = url->cfg->cur_path;