From e06d1b520e9671c57e1bf04cb9c0ef8d278f4985 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Thu, 23 Jul 2026 09:02:17 +0200 Subject: [PATCH] fix(daemon,win): give conflict-log share collisions a contention budget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The private-file prepare treated slow CONTENTION like permanent OBSTRUCTION: one 250 ms deadline for both. With many concurrent appenders on a loaded, sanitized runner the last writer can legitimately wait longer than that behind its peers' brief exclusive windows, and the concurrent-append guard correctly failed a dropped conflict event (dry run: one appender of the pack lost). Transient share/lock violations now retry against a 2000 ms contention budget while every other error class keeps the 250 ms fast-fail — a rejected client is still waiting behind this on the hello path. Signed-off-by: Martin Vogel --- src/daemon/service.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/daemon/service.c b/src/daemon/service.c index 777700f88..13413be2f 100644 --- a/src/daemon/service.c +++ b/src/daemon/service.c @@ -856,12 +856,26 @@ static bool windows_private_file_prepare(const char *directory, const char *base * to the ~16 ms timer granularity, and a permanently obstructed path * (e.g. a directory squatting on it) must fail fast — a rejected client * is waiting behind this on the hello path with its own timeout. */ - ULONGLONG deadline = GetTickCount64() + 250; + ULONGLONG obstruction_deadline = GetTickCount64() + 250; + /* Genuine share collisions get a wider budget than obstruction: with + * many concurrent appenders on a loaded, sanitized CI runner the + * last-in-line writer can legitimately spend more than 250 ms behind + * its peers' brief exclusive windows, and the no-drop contract for + * conflict events outranks latency there — while a permanently + * obstructed path (a directory squatting on the name) still fails + * inside 250 ms, because a rejected client is waiting behind this on + * the hello path with its own timeout. */ + ULONGLONG contention_deadline = GetTickCount64() + 2000; for (;;) { + SetLastError(ERROR_SUCCESS); FILE *file = cbm_daemon_ipc_private_log_open(directory, base, SIZE_MAX); if (file) { return fclose(file) == 0; } + DWORD open_error = GetLastError(); + bool transient_collision = + open_error == ERROR_SHARING_VIOLATION || open_error == ERROR_LOCK_VIOLATION; + ULONGLONG deadline = transient_collision ? contention_deadline : obstruction_deadline; if (GetTickCount64() >= deadline) { return false; }