]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: haload: add -T to add a think time after each response
authorFrederic Lecaille <flecaille@haproxy.com>
Tue, 25 Aug 2026 13:55:36 +0000 (15:55 +0200)
committerFrederic Lecaille <flecaille@haproxy.com>
Wed, 26 Aug 2026 16:16:35 +0000 (18:16 +0200)
Add -T <time>, like h1load's option of the same name: after a
response, wait <time> ms (with a bit of random jitter) before sending
the next request, instead of sending it right away. This simulates a
real user pausing between actions.

-T is positional, like -post or -head: it applies to the URLs that
follow it on the command line, not to all of them.

If -R is also set, the wait used is the larger of the two: the rate
limit and the think time never fight each other, whichever asks for
more waiting wins.

-ee takes over as soon as an error is detected: -T is ignored while it
is still probing, and applies again once probing is done and the
normal load resumes.

doc/haload.txt
include/haproxy/haload.h
src/haload.c
src/haload_init.c

index d217c88903df11b86895e3434b2238ead8ab82aa..edaed1fefa0e9be8b0add969285a2e78c1f1831c 100644 (file)
@@ -81,6 +81,7 @@ HALoad displays its usage when run without argument or wrong arguments:
             -C               dump the configuration and exit
             -H "foo:bar"     add this header name and value
             -R <rate>        limit to this many request attempts per second (0)
+            -T <time>        think time in ms after a response (0) (*)
             -v               shows version
             --defaults <str> add a string to default section
             --global <str>   add a string to global section
index b66c0e4f636cfd2c7de5f92fb738dc4dba14ec1b..271cee9392c82d11eadb80597a8e8b06c10e3f60 100644 (file)
@@ -34,6 +34,7 @@ struct hld_url_cfg {
        int ssl;
        int is_quic;
        int h2c;
+       int thnk_time;
        enum hld_http_ver http_ver;
        char *addr;
        char *raw_addr; // used only to set the host header value
@@ -83,6 +84,7 @@ extern int arg_rcon;
 extern int arg_slow;
 extern int arg_serr;
 extern int arg_usr;
+extern int arg_thnk;
 extern int arg_thrd;
 extern int arg_wait;
 
index e2cd6a71dbe772c18b13bbeca8f9fb182a0dd8a4..f303790fe80a7eab66513e81938475334d037c55 100644 (file)
@@ -107,6 +107,7 @@ int arg_rcon = -1;     // max requests per conn
 int arg_reqs = -1;     // max total requests
 int arg_serr;          // 1 = stop on first error (-e), 2 = probe and ramp (-ee)
 int arg_slow;          // slow start: delay in milliseconds
+int arg_thnk;          // think time after a reponse (ms)
 int arg_thrd;          // number of threads
 int arg_usr = 1;       // number of users
 int arg_wait = 10000;  // I/O time out (ms)
@@ -1432,8 +1433,11 @@ static inline uint32_t hld_thr_rate_max(struct hld_thr_info *ti, int rate)
        return hld_thr_rate_share(rate);
 }
 
-/* Schedule <usr> user, depending on <rate> conn/req rate value */
-static inline void hld_usr_schedule(struct hld_usr *usr, int rate)
+/* Schedule <usr> user, depending on <rate> conn/req rate value and on
+ * <think>, an already-computed think time in ms (0 if none). The wait
+ * used is the larger of the two, same as h1load's -T.
+ */
+static inline void hld_usr_schedule(struct hld_usr *usr, int rate, uint32_t think)
 {
        uint32_t max, wait;
        struct hld_thr_info *ti = &thrs_info[tid];
@@ -1441,17 +1445,19 @@ static inline void hld_usr_schedule(struct hld_usr *usr, int rate)
        hld_thr_sync_ramp_gen(ti);
        max = hld_thr_rate_max(ti, rate);
 
-       if (!max) {
+       if (!max)
                /* this thread has no budget right now (its exact share of
                 * <rate> is 0, or it's still ramping up under -s): nothing
                 * to send from here, just check back shortly.
                 */
-               task_schedule(usr->task, tick_add(now_ms, MS_TO_TICKS(1000)));
-               return;
-       }
+               wait = 1000;
+       else
+               wait = hld_next_event_delay(&ti->req_rate, max,
+                                           ti->curusrs - ti->cur_req, date);
+
+       if (think > wait)
+               wait = think;
 
-       wait = hld_next_event_delay(&ti->req_rate, max,
-                                   ti->curusrs - ti->cur_req, date);
        task_schedule(usr->task, tick_add(now_ms, MS_TO_TICKS(wait)));
 }
 
@@ -1645,7 +1651,7 @@ struct task *hld_strm_task(struct task *t, void *context, unsigned int state)
        /* Note that the user task will release all the expired streams
         * attached to it.
         */
-       if (!HA_ATOMIC_LOAD(&arg_rate)) {
+       if (!HA_ATOMIC_LOAD(&arg_rate) && !url->cfg->thnk_time) {
                task_wakeup(usr->task, TASK_WOKEN_IO);
                if (LIST_ISEMPTY(&usr->strms))
                        usr->task->expire = TICK_ETERNITY;
@@ -1659,7 +1665,20 @@ struct task *hld_strm_task(struct task *t, void *context, unsigned int state)
                }
        }
        else {
-               hld_usr_schedule(usr, HA_ATOMIC_LOAD(&arg_rate));
+               int rate = HA_ATOMIC_LOAD(&arg_rate);
+               uint32_t think = 0;
+
+               /* -ee takes over as soon as an error is detected: ignore -T
+                * while it is still probing. -R and -T only apply again once
+                * probing is done and the normal load resumes.
+                */
+               if (!HA_ATOMIC_LOAD(&err_ramp_active) && url->cfg->thnk_time)
+                       think = url->cfg->thnk_time * (4096 - 128 + statistical_prng_range(256)) / 4096;
+
+               if (rate)
+                       hld_usr_schedule(usr, rate, think);
+               else
+                       task_schedule(usr->task, tick_add(now_ms, MS_TO_TICKS(think)));
        }
 
        goto leave;
@@ -1693,7 +1712,7 @@ struct task *hld_strm_task(struct task *t, void *context, unsigned int state)
                if (!HA_ATOMIC_LOAD(&arg_rate))
                        task_wakeup(usr->task, TASK_WOKEN_IO);
                else
-                       hld_usr_schedule(usr, HA_ATOMIC_LOAD(&arg_rate));
+                       hld_usr_schedule(usr, HA_ATOMIC_LOAD(&arg_rate), 0);
 
                if (arg_serr == 1) {
                        usr->flags |= HLD_USR_FL_STOP;
index 4182dc667d69200e2a67a4a3ed16df515a812cfb..5ec9837d4a9c7195c646a9a5cf1624e07b40f817 100644 (file)
@@ -47,6 +47,7 @@ static void  hld_usage(char *name, int argc)
                "        -F               merge send() with connect's ACK\n"
                "        -H \"foo:bar\"   add this header name and value\n"
                "        -R <rate>        limit to this many request attempts per second (0)\n"
+               "        -T <time>        think time in ms after a response (0) (*)\n"
                "        -v               shows version\n"
                "        --defaults <str> add a string to default section\n"
                "        --global <str>   add a string to global section\n"
@@ -304,6 +305,7 @@ static struct hld_url_cfg *hld_alloc_url(char *url, int is_quic,
        hld_url_cfg->ssl = ssl;
        hld_url_cfg->is_quic = is_quic;
        hld_url_cfg->h2c = h2c_param;
+       hld_url_cfg->thnk_time = arg_thnk;
        hld_url_cfg->http_ver = http_ver;
        hld_url_cfg->addr = addr;
        hld_url_cfg->raw_addr = raw_addr;
@@ -670,6 +672,10 @@ void haproxy_init_args(int argc, char **argv)
                                opt++;
                                hld_parse_long(&arg_rate, opt, &argc, &argv);
                        }
+                       else if (*opt == 'T') {
+                               opt++;
+                               hld_parse_long(&arg_thnk, opt, &argc, &argv);
+                       }
                        else if (*opt == 'v') {
                                /* empty option */
                                if (*(opt + 1))