diff --git a/src/main.c b/src/main.c index c5c936e49..92c667b90 100644 --- a/src/main.c +++ b/src/main.c @@ -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 =