From f124293024877083861aaa4ef82e0b93038eb529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 12 Jun 2025 17:41:13 +0200 Subject: [PATCH] local-binary-cache-store: Add retry loop to upsertFile Handle race conditions in binary cache file creation by retrying with a new temporary name on EEXIST errors. Uses existing makeTempPath which already has an atomic counter. --- src/libstore/local-binary-cache-store.cc | 33 +++++++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 2f23135fa..97866c0ca 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -58,13 +58,32 @@ protected: const std::string & mimeType) override { auto path2 = config->binaryCacheDir + "/" + path; - static std::atomic counter{0}; - Path tmp = fmt("%s.tmp.%d.%d", path2, getpid(), ++counter); - AutoDelete del(tmp, false); - StreamToSourceAdapter source(istream); - writeFile(tmp, source); - std::filesystem::rename(tmp, path2); - del.cancel(); + + /* Retry loop for handling race conditions */ + while (true) { + Path tmp = makeTempPath(dirOf(path2), baseNameOf(path2) + ".tmp"); + AutoDelete del(tmp, false); + + StreamToSourceAdapter source(istream); + try { + writeFile(tmp, source); + } catch (Error & e) { + e.addTrace({}, "while writing to temporary file '%s' for '%s'", tmp, path2); + } + + try { + std::filesystem::rename(tmp, path2); + del.cancel(); + break; /* Success! */ + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::file_exists) { + /* Race condition: another process created the same file. + Try again with a different name. */ + continue; + } + throw SysError("renaming '%s' to '%s'", tmp, path2); + } + } } void getFile(const std::string & path, Sink & sink) override