From 484321168432ce52ddf3263de4f8f2ce32c03af1 Mon Sep 17 00:00:00 2001
From: Alexander Stephan <alexander.stephan@sap.com>
Date: Thu, 11 Dec 2025 11:19:10 +0000
Subject: [PATCH 3/4] BUG/MINOR: mworker/cli: avoid duplicates in 'show proc'

After commit 594408c, related to ticket #3204, the 'show proc' logic has
been adjusted to be able to print more than 202 processes. However, this
fix can lead to diplication of entries in case they have the same
timestamp.

To fix this, we now flush the CLI output only when the worker timestamp
changes, batching entries that share the same second. We apply the same
logic to old workers and update ctx->next_uptime at each flush and after
the final group to resume correctly.
---
 src/mworker.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/src/mworker.c b/src/mworker.c
index 3d6ce3a26..5ee08b06a 100644
--- a/src/mworker.c
+++ b/src/mworker.c
@@ -816,6 +816,7 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 	struct mworker_proc *child;
 	int old = 0;
 	int up = date.tv_sec - proc_self->timestamp;
+	int prev_ts = -1;
 	struct cli_showproc_ctx *ctx = appctx->svcctx;
 	char *uptime = NULL;
 	char *reloadtxt = NULL;
@@ -846,7 +847,6 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 	if (ctx->next_uptime == 0)
 		chunk_appendf(&trash, "# workers\n");
 	list_for_each_entry(child, &proc_list, list) {
-
 		/* don't display current worker if we only need the next ones */
 		if (ctx->next_uptime != 0)
 			continue;
@@ -862,6 +862,13 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 			old++;
 			continue;
 		}
+		if (prev_ts != -1 && child->timestamp != prev_ts) {
+			if (applet_putchk(appctx, &trash) == -1)
+				return 0;
+			chunk_reset(&trash);
+		}
+		prev_ts = child->timestamp;
+
 		memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
 		chunk_appendf(&trash, "%-15u %-15s %-15d %-15s %-15s", child->pid, "worker", child->reloads, uptime, child->version);
 		if (ctx->debug)
@@ -873,13 +880,18 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 	if (applet_putchk(appctx, &trash) == -1)
 		return 0;
 
+	/* ensure a clean buffer before starting the old-workers section */
+	chunk_reset(&trash);
+
 	/* displays old processes */
 	if (old || ctx->next_uptime) { /* there's more */
 		if (ctx->next_uptime == 0)
 			chunk_appendf(&trash, "# old workers\n");
+		/* reset timestamp grouping for old workers */
+		prev_ts = -1;
 		list_for_each_entry(child, &proc_list, list) {
 			up = date.tv_sec - child->timestamp;
-			if (up <= 0) /* must never be negative because of clock drift */
+			if (up < 0) /* must never be negative because of clock drift */
 				up = 0;
 
 			if (child->timestamp < ctx->next_uptime)
@@ -889,6 +901,14 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 				continue;
 
 			if (child->options & PROC_O_LEAVING) {
+				if (prev_ts != -1 && child->timestamp != prev_ts) {
+					ctx->next_uptime = prev_ts;
+					if (applet_putchk(appctx, &trash) == -1)
+						return 0;
+					chunk_reset(&trash);
+				}
+				prev_ts = child->timestamp;
+
 				memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
 				chunk_appendf(&trash, "%-15u %-15s %-15d %-15s %-15s", child->pid, "worker", child->reloads, uptime, child->version);
 				if (ctx->debug)
@@ -896,10 +916,10 @@ static int cli_io_handler_show_proc(struct appctx *appctx)
 				chunk_appendf(&trash, "\n");
 				ha_free(&uptime);
 			}
-
+		}
+		if (prev_ts != -1) {
 			/* start from there if there's not enough place */
-			ctx->next_uptime = child->timestamp;
-
+			ctx->next_uptime = prev_ts;
 			if (applet_putchk(appctx, &trash) == -1)
 				return 0;
 		}
-- 
2.43.7

