feat(daemon): CBM_STARTUP_TIMEOUT_MS overrides the startup budget

A fixed 30 s startup budget races whatever else the host is doing;
under a parallel test wave a cold daemon loses through no fault of
its own and every client gives up with 'could not start within
30000 ms'. The env override (clamped to 1 s..10 min, malformed
values fall back to the default) lets loaded hosts widen the budget
without changing the default behavior anywhere else. Applied to the
MCP/CLI client bootstrap configs, the upgrade drain request, and
the client cohort-acquire deadline.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-08-27 01:45:32 +02:00
parent e14624d681
commit f450f9b13e
+25 -4
View File
@@ -51,6 +51,8 @@ enum {
* that never finishes, so a command cannot hang indefinitely. */
MAIN_STARTUP_CONTENTION_CEILING_MS = 120000,
MAIN_MCP_STARTUP_TIMEOUT_MS = 30000,
MAIN_STARTUP_TIMEOUT_MIN_MS = 1000,
MAIN_STARTUP_TIMEOUT_MAX_MS = 600000,
MAIN_REQUEST_TIMEOUT_MS = 24 * 60 * 60 * 1000,
MAIN_HOOK_CONNECT_TIMEOUT_MS = 250,
MAIN_HOOK_REQUEST_TIMEOUT_MS = 1500,
@@ -60,6 +62,25 @@ enum {
MAIN_COORDINATION_CLEANUP_MS = 500,
PARENT_WATCHDOG_STACK_SIZE = 64 * CBM_SZ_1K, /* watchdog only polls — tiny stack suffices */
};
/* Daemon startup budget, overridable for loaded hosts: a fixed 30 s races
* whatever else the machine is doing (parallel test waves, CI legs), and a
* cold daemon on a starved host loses through no fault of its own.
* CBM_STARTUP_TIMEOUT_MS, clamped to [1s, 10min]; malformed -> default. */
static uint32_t main_startup_timeout_ms(void) {
const char *raw = getenv("CBM_STARTUP_TIMEOUT_MS");
if (!raw || !raw[0])
return MAIN_MCP_STARTUP_TIMEOUT_MS;
char *end = NULL;
long value = strtol(raw, &end, 10);
if (!end || *end != '\0' || value <= 0)
return MAIN_MCP_STARTUP_TIMEOUT_MS;
if (value < MAIN_STARTUP_TIMEOUT_MIN_MS)
return MAIN_STARTUP_TIMEOUT_MIN_MS;
if (value > MAIN_STARTUP_TIMEOUT_MAX_MS)
return MAIN_STARTUP_TIMEOUT_MAX_MS;
return (uint32_t)value;
}
#define SLEN(s) (sizeof(s) - 1)
#include "foundation/log.h"
#include "foundation/diagnostics.h"
@@ -1564,7 +1585,7 @@ static cbm_daemon_bootstrap_status_t main_client_bootstrap_with_upgrade(
cbm_daemon_runtime_activation_result_t drain;
if (!cbm_daemon_runtime_request_activation_shutdown(config->endpoint, config->identity,
CBM_DAEMON_RUNTIME_ACTIVATION_UPDATE,
MAIN_MCP_STARTUP_TIMEOUT_MS, &drain) ||
main_startup_timeout_ms(), &drain) ||
!drain.accepted) {
(void)fprintf(stderr, "codebase-memory-mcp: the active daemon did not accept the "
"upgrade drain; run `codebase-memory-mcp daemon stop`\n");
@@ -1596,7 +1617,7 @@ static char *main_local_cli_daemon_execute(const char *tool_name, const char *ar
.identity = &identity,
.executable_path = executable_path,
.connect_timeout_ms = MAIN_CONNECT_TIMEOUT_MS,
.startup_timeout_ms = MAIN_MCP_STARTUP_TIMEOUT_MS,
.startup_timeout_ms = main_startup_timeout_ms(),
};
cbm_daemon_bootstrap_result_t bootstrap;
cbm_daemon_bootstrap_status_t status = main_client_bootstrap_with_upgrade(&config, &bootstrap);
@@ -2873,7 +2894,7 @@ int main(int argc, char **argv) {
? cbm_version_cohort_acquire(client_cohort_manager, &identity,
main_deadline_after(role == CBM_DAEMON_PROCESS_HOOK_CLIENT
? MAIN_HOOK_REQUEST_TIMEOUT_MS
: MAIN_MCP_STARTUP_TIMEOUT_MS),
: main_startup_timeout_ms()),
&client_cohort_lease, &client_cohort_conflict)
: CBM_VERSION_COHORT_IO;
if (client_cohort_status != CBM_VERSION_COHORT_OK) {
@@ -2935,7 +2956,7 @@ int main(int argc, char **argv) {
.identity = &identity,
.executable_path = executable_path,
.connect_timeout_ms = MAIN_CONNECT_TIMEOUT_MS,
.startup_timeout_ms = MAIN_MCP_STARTUP_TIMEOUT_MS,
.startup_timeout_ms = main_startup_timeout_ms(),
};
cbm_daemon_bootstrap_result_t bootstrap_result;
cbm_daemon_bootstrap_status_t bootstrap_status =