From 90c254d08570f093224c127f3f5f946dd1c32d66 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 22:00:04 +0000 Subject: [PATCH 1/2] Initial plan From d6d38f22f4b6a6efc7da1181581c97d4797e01d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 22:52:10 +0000 Subject: [PATCH 2/2] Add idempotent new-hist support Agent-Logs-Url: https://github.com/stellar/stellar-core/sessions/9dfeca7f-4f5c-412a-9a88-f98dc0c3aca7 Co-authored-by: anupsdf <127880479+anupsdf@users.noreply.github.com> --- docs/software/commands.md | 10 ++++++---- src/history/HistoryArchiveManager.cpp | 10 +++++++++- src/history/HistoryArchiveManager.h | 3 ++- src/history/test/HistoryTests.cpp | 22 ++++++++++++++++++++++ src/main/ApplicationUtils.cpp | 6 ++++-- src/main/ApplicationUtils.h | 3 ++- src/main/CommandLine.cpp | 7 ++++++- 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/docs/software/commands.md b/docs/software/commands.md index c4dde5790a..dc73ec4ba1 100644 --- a/docs/software/commands.md +++ b/docs/software/commands.md @@ -148,10 +148,12 @@ Command options can only by placed after command. * **load-xdr **: Load an XDR bucket file, for testing. * **new-db**: Clears the local database and resets it to the genesis ledger. If you connect to the network after that it will catch up from scratch. -* **new-hist ...**: Initialize the named history archives - HISTORY-LABEL. HISTORY-LABEL should be one of the history archives you have - specified in the stellar-core.cfg. This will write a - `.well-known/stellar-history.json` file in the archive root. +* **new-hist [--idempotent] ...**: Initialize the named + history archives HISTORY-LABEL. HISTORY-LABEL should be one of the history + archives you have specified in the stellar-core.cfg. This will write a + `.well-known/stellar-history.json` file in the archive root. With + **--idempotent**, the command succeeds even if the archive is already + initialized. * **offline-info**: Returns an output similar to `--c info` for an offline instance, but written directly to standard output (ignoring log levels). * **print-xdr **: Pretty-print a binary file containing an XDR diff --git a/src/history/HistoryArchiveManager.cpp b/src/history/HistoryArchiveManager.cpp index 20e69a070f..7aefee3a84 100644 --- a/src/history/HistoryArchiveManager.cpp +++ b/src/history/HistoryArchiveManager.cpp @@ -197,7 +197,8 @@ HistoryArchiveManager::getCheckLedgerHeaderWork( } bool -HistoryArchiveManager::initializeHistoryArchive(std::string const& arch) const +HistoryArchiveManager::initializeHistoryArchive(std::string const& arch, + bool idempotent) const { auto archive = getHistoryArchive(arch); if (!archive) @@ -215,6 +216,13 @@ HistoryArchiveManager::initializeHistoryArchive(std::string const& arch) const ws.executeWork(0, archive, "hist-init", 0); if (getHas->getState() == BasicWork::State::WORK_SUCCESS) { + if (idempotent) + { + CLOG_INFO(History, + "History archive '{}' already initialized; skipping", + arch); + return true; + } CLOG_ERROR(History, "History archive '{}' already initialized!", arch); return false; } diff --git a/src/history/HistoryArchiveManager.h b/src/history/HistoryArchiveManager.h index 0b310a9cfa..da1e8727d6 100644 --- a/src/history/HistoryArchiveManager.h +++ b/src/history/HistoryArchiveManager.h @@ -39,7 +39,8 @@ class HistoryArchiveManager // Initialize a named history archive by writing // .well-known/stellar-history.json to it. - bool initializeHistoryArchive(std::string const& arch) const; + bool initializeHistoryArchive(std::string const& arch, + bool idempotent = false) const; // Returns whether or not the HistoryManager has any writable history // archives (those configured with both a `get` and `put` command). diff --git a/src/history/test/HistoryTests.cpp b/src/history/test/HistoryTests.cpp index 0e811557f4..d5bd9e97bf 100644 --- a/src/history/test/HistoryTests.cpp +++ b/src/history/test/HistoryTests.cpp @@ -2190,6 +2190,28 @@ TEST_CASE("initialize existing history store fails", "[history]") } } +TEST_CASE("initialize existing history store idempotently succeeds", + "[history]") +{ + Config cfg(getTestConfig(0, Config::TESTDB_BUCKET_DB_PERSISTENT)); + TmpDirHistoryConfigurator tcfg; + cfg = tcfg.configure(cfg, true); + + { + VirtualClock clock; + Application::pointer app = createTestApplication(clock, cfg); + REQUIRE(app->getHistoryArchiveManager().initializeHistoryArchive( + tcfg.getArchiveDirName())); + } + + { + VirtualClock clock; + Application::pointer app = createTestApplication(clock, cfg); + REQUIRE(app->getHistoryArchiveManager().initializeHistoryArchive( + tcfg.getArchiveDirName(), true)); + } +} + TEST_CASE("Catchup failure recovery with buffered checkpoint", "[history][catchup]") { diff --git a/src/main/ApplicationUtils.cpp b/src/main/ApplicationUtils.cpp index cc6a2cc4c4..fc512824b7 100644 --- a/src/main/ApplicationUtils.cpp +++ b/src/main/ApplicationUtils.cpp @@ -1030,7 +1030,8 @@ genSeed() } int -initializeHistories(Config cfg, std::vector const& newHistories) +initializeHistories(Config cfg, std::vector const& newHistories, + bool idempotent) { VirtualClock clock; cfg.setNoListen(); @@ -1038,7 +1039,8 @@ initializeHistories(Config cfg, std::vector const& newHistories) for (auto const& arch : newHistories) { - if (!app->getHistoryArchiveManager().initializeHistoryArchive(arch)) + if (!app->getHistoryArchiveManager().initializeHistoryArchive( + arch, idempotent)) return 1; } return 0; diff --git a/src/main/ApplicationUtils.h b/src/main/ApplicationUtils.h index c8a3e8a997..970f2f3de7 100644 --- a/src/main/ApplicationUtils.h +++ b/src/main/ApplicationUtils.h @@ -52,7 +52,8 @@ int rebuildLedgerFromBuckets(Config cfg); #endif void genSeed(); int initializeHistories(Config cfg, - std::vector const& newHistories); + std::vector const& newHistories, + bool idempotent = false); void writeCatchupInfo(Json::Value const& catchupInfo, std::string const& outputFile); int catchup(Application::pointer app, CatchupConfiguration cc, diff --git a/src/main/CommandLine.cpp b/src/main/CommandLine.cpp index 2c870bed8c..a16a93b4e2 100644 --- a/src/main/CommandLine.cpp +++ b/src/main/CommandLine.cpp @@ -1501,13 +1501,18 @@ runNewHist(CommandLineArgs const& args) { CommandLine::ConfigOption configOption; std::vector newHistories; + bool idempotent = false; return runWithHelp(args, {configurationParser(configOption), + clara::Opt{idempotent}["--idempotent"]( + "succeed if the history archive is already " + "initialized"), requiredArgParser(newHistories, "HISTORY-LABEL")}, [&] { return initializeHistories(configOption.getConfig(), - newHistories); + newHistories, + idempotent); }); }