]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: haload: keep old-scheduled users under -ee's floor rate
authorFrederic Lecaille <flecaille@haproxy.com>
Mon, 24 Aug 2026 17:14:06 +0000 (19:14 +0200)
committerFrederic Lecaille <flecaille@haproxy.com>
Mon, 24 Aug 2026 17:16:05 +0000 (19:16 +0200)
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.

src/haload.c

index 1423cc1ab5e7806721173289d2e518645a025c92..e2cd6a71dbe772c18b13bbeca8f9fb182a0dd8a4 100644 (file)
@@ -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;