]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: config: add a "worker-id" global keyword to identify the worker
authorWilly Tarreau <w@1wt.eu>
Tue, 15 Sep 2026 11:46:53 +0000 (13:46 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 16 Sep 2026 16:00:51 +0000 (18:00 +0200)
The worker process now also has a unique identifier. Contrary to the
master's one, it may be replaced using the new "worker-id" keyword in
the global section. Like for the master, a time-ordered UUID (version 7)
is generated at boot (allowing to compare older vs newer process).

The identifier is immediately exported as HAPROXY_WORKER_ID so that it
may be referenced from the worker's configuration and be inherited by
the processes we may fork (e.g. external checks). It may even be
referenced by the worker-id directive itself to combine a static part
with the dynamically generated one.

doc/configuration.txt
include/haproxy/global-t.h
src/cfgparse-global.c
src/haproxy.c

index 7740b52e56ed3381e82e60c8808086ef8b93d473..6662168f6e461562eb23fc6dc6b6cc2d50b9262d 100644 (file)
@@ -981,6 +981,7 @@ Some variables are marked as not usable and not modifiable:
   * HAPROXY_CLI
   * HAPROXY_MASTER_CLI
   * HAPROXY_MASTER_ID
+  * HAPROXY_WORKER_ID
   * HAPROXY_LOCALPEER
 
 Their values are undefined during configuration parsing, they are set later
@@ -1003,6 +1004,7 @@ modes:
   | HAPROXY_CLI               |    |    |      |     |     |  X  |
   | HAPROXY_MASTER_CLI        |    |    |      |     |  X  |     |
   | HAPROXY_MASTER_ID         |    | X  |      |     |  X  |  X  |
+  | HAPROXY_WORKER_ID         |    | X  |      |     |     |  X  |
   | HAPROXY_LOCALPEER         |    | X  |      |     |     |  X  |
   | HAPROXY_HTTP_LOG_FMT      |    | X  |      |  X  |     |     |
   | HAPROXY_HTTP_CLF_LOG_FMT  |    | X  |      |  X  |     |     |
@@ -1071,6 +1073,17 @@ The variables in question are the following:
     the one of the proxies' and servers' "guid": at most 127 characters among
     letters, digits, '.', ':', '-' and '_'.
 
+  * HAPROXY_WORKER_ID: unique identifier of the worker process. It is randomly
+    generated at boot on the worker process, and may be overwritten by the
+    "worker-id" keyword in the global section. The generated random is changed
+    on every reload.
+
+ Its format is the same as the one of HAPROXY_MASTER_ID above. It is
+    set before the worker reads the rest of its configuration, so it may be
+    referenced from there, and being part of the process' environment it is
+    also visible to Lua (os.getenv()) and inherited by the processes launched
+    from the worker, such as external health checks.
+
   * HAPROXY_STARTUP_VERSION: contains the version used to start, in master-
     worker mode this is the version which was used to start the master, even
     after updating the binary and reloading.
@@ -1858,6 +1871,7 @@ The following keywords are supported in the "global" section :
    - unix-bind
    - unsetenv
    - user
+   - worker-id
    - wurfl-cache-size
    - wurfl-data-file
    - wurfl-information-list
@@ -3812,6 +3826,28 @@ node <name>
   nodes, it becomes easy to immediately spot what server is handling the
   traffic.
 
+worker-id <id>
+  Replaces the default worker process identifier with <id>. The format rules
+  are identical to the proxy and server "guid" attributes: up to 127 characters
+  containing only alphanumeric values, ".", ":", "-", and "_".
+
+  HAProxy generates a time-ordered UUIDv7 for the worker at boot before parsing
+  the configuration. This default identifier changes on every reload, embedding
+  the process start time to enable chronological sorting of workers in logs for
+  example.
+
+  Using worker-id overwrites this identifier and instantly updates the
+  HAPROXY_WORKER_ID environment variable. Any subsequent configuration
+  directive (e.g. "log-format") will use the replaced value. The directive
+  itself can reference HAPROXY_WORKER_ID before it is overwritten, allowing
+  to combine for example a node name with the generated UUID.
+
+  Example:
+      worker-id "pop7-node1-inst3-${HAPROXY_WORKER_ID}"
+
+  The identifier is exported as the HAPROXY_WORKER_ID environment variable (see
+  section 2.3). See also HAPROXY_MASTER_ID in section 2.3.
+
 wurfl-cache-size <size>
   Sets the WURFL Useragent cache size. For faster lookups, already processed user
   agents are kept in a LRU cache :
index 6d26cb2fc0adeab416a6095cfe2672bae2f8c1a5..239f11ba5663c214f2bbf1450c845abb2761f205 100644 (file)
@@ -162,6 +162,7 @@ struct global {
        char *pidfile;
        char *node, *desc;              /* node name & description */
        char *master_id;                /* master process' unique ID, preserved upon reload */
+       char *worker_id;                /* worker process' unique ID, renewed upon reload */
        int localpeer_cmdline;          /* whether or not the commandline "-L" was set */
        int fd_hard_limit;              /* hard limit on ulimit-n : 0=unset */
        struct buffer log_tag;           /* name for syslog */
index af1acb40f1de8d0ef4aeb7075f37b722c13698e7..71786a7f81dcabb0e2a92bc75aa52676bc831709 100644 (file)
@@ -21,6 +21,7 @@
 #endif
 #include <haproxy/compression.h>
 #include <haproxy/global.h>
+#include <haproxy/guid.h>
 #include <haproxy/log.h>
 #include <haproxy/peers.h>
 #include <haproxy/protocol.h>
@@ -1903,6 +1904,48 @@ static int cfg_parse_global_stress_level(char **args, int section_type, struct p
        return 0;
 }
 
+/* Parses the "worker-id" keyword, which assigns a unique identifier to this
+ * worker process. It is preset to a UUID v7 generated at boot (see
+ * init_worker_id()). It is parsed only by the worker and the environment
+ * variable is set on the fly so that its value is instantly known from next
+ * directives making use of "$HAPROXY_WORKER_ID" (e.g. log-format).
+ */
+static int cfg_parse_global_worker_id(char **args, int section_type, struct proxy *curpx,
+                                      const struct proxy *defpx, const char *file, int line,
+                                      char **err)
+{
+       char *errmsg = NULL;
+
+       if (too_many_args(1, args, err, NULL))
+               return -1;
+
+       if (*(args[1]) == 0) {
+               memprintf(err, "'%s' expects an identifier as an argument.", args[0]);
+               return -1;
+       }
+
+       if (!guid_is_valid_fmt(args[1], &errmsg)) {
+               memprintf(err, "'%s': %s.", args[0], errmsg);
+               free(errmsg);
+               return -1;
+       }
+
+       if (setenv("HAPROXY_WORKER_ID", args[1], 1) != 0) {
+               memprintf(err, "'%s' failed to set HAPROXY_WORKER_ID to '%s' : %s.\n",
+                         args[0], args[1], strerror(errno));
+               return -1;
+       }
+
+       ha_free(&global.worker_id);
+       global.worker_id = strdup(args[1]);
+       if (!global.worker_id) {
+               memprintf(err, "cannot allocate memory for '%s'.", args[0]);
+               return -1;
+       }
+
+       return 0;
+}
+
 static struct cfg_kw_list cfg_kws = {ILH, {
        { CFG_GLOBAL, "chroot", cfg_parse_global_chroot },
        { CFG_GLOBAL, "daemon", cfg_parse_global_mode, KWF_DISCOVERY } ,
@@ -1959,6 +2002,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
        { CFG_GLOBAL, "tune.streams-elasticity", cfg_parse_global_tune_opts },
        { CFG_GLOBAL, "tune.takeover-other-tg-connections", cfg_parse_global_tune_opts },
        { CFG_GLOBAL, "unsetenv", cfg_parse_global_env_opts, KWF_DISCOVERY },
+       { CFG_GLOBAL, "worker-id", cfg_parse_global_worker_id },
        { CFG_GLOBAL, "zero-warning", cfg_parse_global_mode, KWF_DISCOVERY },
        { 0, NULL, NULL },
 }};
index ffe0d138b1eddf3c1f55dbbfda7884f8640609dc..317855bd9152bf9ac48d91061258bbfb0d3fb4d7 100644 (file)
@@ -2170,6 +2170,31 @@ static void step_init_1()
        }
 }
 
+/* Assigns a unique ID to the worker process if it does not have one yet, i.e.
+ * if "worker-id" was not found in the configuration, and exports it to the
+ * environment so that it may be referenced as $HAPROXY_WORKER_ID from the
+ * configuration, and be inherited by the processes we fork. A time-ordered
+ * UUID v7 is used so that a newly started worker may be told from the previous
+ * ones, which may still be finishing their work after a reload. This must not
+ * be called in the master, which has its own ID instead, preserved across
+ * reloads (see mworker_prepare_master()).
+ */
+static void init_worker_id(void)
+{
+       if (!global.worker_id) {
+               char uuid[40];
+               struct buffer out = b_make(uuid, sizeof(uuid), 0, 0);
+
+               ha_generate_uuid_v7(&out);
+               global.worker_id = strdup(uuid);
+               if (!global.worker_id) {
+                       ha_alert("Cannot allocate memory for the worker's ID.\n");
+                       exit(EXIT_FAILURE);
+               }
+       }
+       setenv("HAPROXY_WORKER_ID", global.worker_id, 1);
+}
+
 /*
  * This is a second part of the late init (previous init() function). It should
  * be called after the stage, when all basic runtime modes (daemon, master-worker)
@@ -2883,6 +2908,7 @@ void deinit(void)
        ha_free(&global.node);
        ha_free(&global.desc);
        ha_free(&global.master_id);
+       ha_free(&global.worker_id);
        ha_free(&oldpids);
        ha_free(&old_argv);
        ha_free(&localpeer);
@@ -3629,6 +3655,9 @@ int main(int argc, char **argv)
                 */
                setenv("HAPROXY_LOCALPEER", localpeer, 1);
 
+               /* Let's make sure HAPROXY_WORKER_ID is set as well */
+               init_worker_id();
+
                /* nbthread and *thread keywords parsers are sensible to global
                 * section position, it should be placed as the first in
                 * the configuration, if these keywords are inside. So, let's