|
|
@@ -183,6 +183,47 @@ uint64_t SnapshotManager::loadLatestSnapshot(MemoryStore& store) {
|
|
|
return loadSnapshot(snapshots.front(), store);
|
|
|
}
|
|
|
|
|
|
+uint64_t SnapshotManager::loadWithFallback(MemoryStore& store,
|
|
|
+ std::filesystem::path* outUsed,
|
|
|
+ size_t* outAttempted) {
|
|
|
+ auto snapshots = listSnapshots(); // already sorted newest-first
|
|
|
+
|
|
|
+ if (outAttempted) *outAttempted = 0;
|
|
|
+
|
|
|
+ if (snapshots.empty()) {
|
|
|
+ throw std::runtime_error("loadWithFallback: no snapshots available");
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string lastError;
|
|
|
+ for (size_t i = 0; i < snapshots.size(); ++i) {
|
|
|
+ const auto& path = snapshots[i];
|
|
|
+ if (outAttempted) (*outAttempted)++;
|
|
|
+
|
|
|
+ try {
|
|
|
+ uint64_t seq = loadSnapshot(path, store);
|
|
|
+ if (outUsed) *outUsed = path;
|
|
|
+ if (i > 0) {
|
|
|
+ spdlog::error(
|
|
|
+ "Loaded fallback snapshot (index {} of {}): {} "
|
|
|
+ "— newer snapshots were corrupt",
|
|
|
+ i, snapshots.size(), path.string());
|
|
|
+ }
|
|
|
+ return seq;
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ lastError = e.what();
|
|
|
+ spdlog::warn("Snapshot load failed for {}: {} — trying next older",
|
|
|
+ path.string(), e.what());
|
|
|
+ // Note: store.clear() is called by loadSnapshot() before writing,
|
|
|
+ // so a partial load followed by retry on the next snapshot is safe.
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ throw std::runtime_error("loadWithFallback: all " +
|
|
|
+ std::to_string(snapshots.size()) +
|
|
|
+ " snapshots failed to load (last error: " +
|
|
|
+ lastError + ")");
|
|
|
+}
|
|
|
+
|
|
|
uint64_t SnapshotManager::loadSnapshot(const std::filesystem::path& path, MemoryStore& store) {
|
|
|
std::ifstream file(path, std::ios::binary);
|
|
|
if (!file) {
|