|
|
@@ -15,6 +15,45 @@
|
|
|
|
|
|
namespace smartbotic::database {
|
|
|
|
|
|
+enum class RecoveryMode {
|
|
|
+ Normal, // level 0 — latest snapshot only, refuse on failure (default)
|
|
|
+ SnapshotFallback, // level 1 — try snapshots newest→oldest
|
|
|
+ WalOnly, // level 2 — ignore snapshots, replay WAL from 0
|
|
|
+ BestEffort, // level 3 — snapshot_fallback then wal_only
|
|
|
+ ForceEmpty // level 4 — start empty, preserve files for forensics
|
|
|
+};
|
|
|
+
|
|
|
+std::string recoveryModeToString(RecoveryMode mode);
|
|
|
+RecoveryMode recoveryModeFromString(const std::string& s); // throws on invalid
|
|
|
+
|
|
|
+/**
|
|
|
+ * Describes how recovery proceeded. A "non-trivial" outcome (anything other than
|
|
|
+ * TrivialSuccess/FreshInstall) triggers auto-readonly mode at the service layer.
|
|
|
+ */
|
|
|
+struct RecoveryOutcome {
|
|
|
+ enum class Kind {
|
|
|
+ TrivialSuccess, // latest snapshot loaded cleanly
|
|
|
+ FreshInstall, // no snapshots, no WAL — clean state
|
|
|
+ SnapshotFellBack, // non-latest snapshot loaded (auto or manual)
|
|
|
+ WalOnlyReplay, // skipped all snapshots, replayed WAL from 0
|
|
|
+ ForcedEmpty, // started empty despite data on disk
|
|
|
+ Failed // recovery refused
|
|
|
+ };
|
|
|
+
|
|
|
+ Kind kind = Kind::TrivialSuccess;
|
|
|
+ std::filesystem::path snapshotUsed;
|
|
|
+ std::filesystem::path expectedSnapshot;
|
|
|
+ std::string failureReason;
|
|
|
+ uint64_t walEntriesReplayed = 0;
|
|
|
+ size_t snapshotsAttempted = 0;
|
|
|
+ size_t snapshotsAvailable = 0;
|
|
|
+
|
|
|
+ bool isNonTrivial() const {
|
|
|
+ return kind != Kind::TrivialSuccess && kind != Kind::FreshInstall;
|
|
|
+ }
|
|
|
+ bool isFailure() const { return kind == Kind::Failed; }
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* Persistence manager coordinates WAL and snapshots for durable storage.
|
|
|
*
|
|
|
@@ -33,6 +72,15 @@ public:
|
|
|
uint64_t maxWalSizeMb = 100; // Trigger snapshot at this WAL size
|
|
|
bool compressionEnabled = true;
|
|
|
uint32_t maxSnapshots = 5;
|
|
|
+
|
|
|
+ // NEW in v1.6.1: snapshot durability
|
|
|
+ bool validateAfterWrite = true;
|
|
|
+ bool cleanupOnlyIfVerified = true;
|
|
|
+
|
|
|
+ // NEW in v1.6.1: recovery
|
|
|
+ RecoveryMode recoveryMode = RecoveryMode::Normal;
|
|
|
+ bool autoEscalate = true;
|
|
|
+ bool allowEmptyOnFreshInstall = true;
|
|
|
};
|
|
|
|
|
|
explicit PersistenceManager(Config config);
|
|
|
@@ -60,11 +108,11 @@ public:
|
|
|
[[nodiscard]] bool isRunning() const { return running_.load(); }
|
|
|
|
|
|
/**
|
|
|
- * Recover state into the memory store.
|
|
|
- * Should be called before start() to load existing data.
|
|
|
- * @return true if recovery succeeded (even if no data to recover)
|
|
|
+ * Recover state into the memory store. Should be called before start().
|
|
|
+ * @return RecoveryOutcome describing how recovery proceeded. Caller
|
|
|
+ * inspects outcome.kind to decide whether to auto-readonly.
|
|
|
*/
|
|
|
- bool recover(MemoryStore& store);
|
|
|
+ RecoveryOutcome recover(MemoryStore& store);
|
|
|
|
|
|
/**
|
|
|
* Log an insert operation.
|