|
@@ -204,6 +204,17 @@ RecoveryOutcome PersistenceManager::recover(MemoryStore& store) {
|
|
|
});
|
|
});
|
|
|
outcome.walEntriesReplayed = replayed;
|
|
outcome.walEntriesReplayed = replayed;
|
|
|
spdlog::info("Replayed {} WAL entries from sequence {}", replayed, fromSequence);
|
|
spdlog::info("Replayed {} WAL entries from sequence {}", replayed, fromSequence);
|
|
|
|
|
+ // Anchor the WAL's sequence_ counter to the snapshot's walSequence.
|
|
|
|
|
+ // Without this, the steady-state outcome of `truncateBefore(walSeq)`
|
|
|
|
|
+ // after every snapshot — which deletes every WAL file because all
|
|
|
|
|
+ // entries have sequence ≤ walSeq — leaves the next boot's
|
|
|
|
|
+ // wal_->open() with no files to read, so sequence_ stays at 0.
|
|
|
|
|
+ // Subsequent appends would then produce sequences 1, 2, 3, …
|
|
|
|
|
+ // that look "older than the snapshot" to the boot AFTER that and
|
|
|
|
|
+ // get silently discarded by recovery's `replay(snapSeq)` filter.
|
|
|
|
|
+ // (Bug fingerprint on Zoe: messages from 10:55–11:41 lost on the
|
|
|
|
|
+ // 11:42 restart; same migration applied twice across the two boots.)
|
|
|
|
|
+ wal_->setSequenceFloor(fromSequence);
|
|
|
{
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(statsMutex_);
|
|
std::lock_guard<std::mutex> lock(statsMutex_);
|
|
|
lastSnapshotSequence_ = fromSequence;
|
|
lastSnapshotSequence_ = fromSequence;
|
|
@@ -355,56 +366,72 @@ RecoveryOutcome PersistenceManager::recover(MemoryStore& store) {
|
|
|
return outcome;
|
|
return outcome;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void PersistenceManager::logInsert(const std::string& collection, const Document& doc) {
|
|
|
|
|
|
|
+void PersistenceManager::logInsert(const std::string& collection, const Document& doc,
|
|
|
|
|
+ const std::string& originNodeId) {
|
|
|
if (!running_.load()) return;
|
|
if (!running_.load()) return;
|
|
|
- uint64_t seq = wal_->append(WriteAheadLog::makeInsertEntry(collection, doc));
|
|
|
|
|
|
|
+ const std::string& origin = originNodeId.empty() ? config_.nodeId : originNodeId;
|
|
|
|
|
+ uint64_t seq = wal_->append(WriteAheadLog::makeInsertEntry(collection, doc, origin));
|
|
|
updateCollectionSequence(collection, seq);
|
|
updateCollectionSequence(collection, seq);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void PersistenceManager::logUpdate(const std::string& collection, const Document& doc) {
|
|
|
|
|
|
|
+void PersistenceManager::logUpdate(const std::string& collection, const Document& doc,
|
|
|
|
|
+ const std::string& originNodeId) {
|
|
|
if (!running_.load()) return;
|
|
if (!running_.load()) return;
|
|
|
- uint64_t seq = wal_->append(WriteAheadLog::makeUpdateEntry(collection, doc));
|
|
|
|
|
|
|
+ const std::string& origin = originNodeId.empty() ? config_.nodeId : originNodeId;
|
|
|
|
|
+ uint64_t seq = wal_->append(WriteAheadLog::makeUpdateEntry(collection, doc, origin));
|
|
|
updateCollectionSequence(collection, seq);
|
|
updateCollectionSequence(collection, seq);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void PersistenceManager::logDelete(const std::string& collection, const std::string& id) {
|
|
|
|
|
|
|
+void PersistenceManager::logDelete(const std::string& collection, const std::string& id,
|
|
|
|
|
+ const std::string& originNodeId) {
|
|
|
if (!running_.load()) return;
|
|
if (!running_.load()) return;
|
|
|
- uint64_t seq = wal_->append(WriteAheadLog::makeDeleteEntry(collection, id));
|
|
|
|
|
|
|
+ const std::string& origin = originNodeId.empty() ? config_.nodeId : originNodeId;
|
|
|
|
|
+ uint64_t seq = wal_->append(WriteAheadLog::makeDeleteEntry(collection, id, origin));
|
|
|
updateCollectionSequence(collection, seq);
|
|
updateCollectionSequence(collection, seq);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void PersistenceManager::logUpsert(const std::string& collection, const Document& doc) {
|
|
|
|
|
|
|
+void PersistenceManager::logUpsert(const std::string& collection, const Document& doc,
|
|
|
|
|
+ const std::string& originNodeId) {
|
|
|
if (!running_.load()) return;
|
|
if (!running_.load()) return;
|
|
|
- uint64_t seq = wal_->append(WriteAheadLog::makeUpsertEntry(collection, doc));
|
|
|
|
|
|
|
+ const std::string& origin = originNodeId.empty() ? config_.nodeId : originNodeId;
|
|
|
|
|
+ uint64_t seq = wal_->append(WriteAheadLog::makeUpsertEntry(collection, doc, origin));
|
|
|
updateCollectionSequence(collection, seq);
|
|
updateCollectionSequence(collection, seq);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void PersistenceManager::logCreateCollection(const std::string& collection, const CollectionOptions& options) {
|
|
|
|
|
|
|
+void PersistenceManager::logCreateCollection(const std::string& collection, const CollectionOptions& options,
|
|
|
|
|
+ const std::string& originNodeId) {
|
|
|
if (!running_.load()) return;
|
|
if (!running_.load()) return;
|
|
|
- uint64_t seq = wal_->append(WriteAheadLog::makeCreateCollectionEntry(collection, options));
|
|
|
|
|
|
|
+ const std::string& origin = originNodeId.empty() ? config_.nodeId : originNodeId;
|
|
|
|
|
+ uint64_t seq = wal_->append(WriteAheadLog::makeCreateCollectionEntry(collection, options, origin));
|
|
|
updateCollectionSequence(collection, seq);
|
|
updateCollectionSequence(collection, seq);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void PersistenceManager::logDropCollection(const std::string& collection) {
|
|
|
|
|
|
|
+void PersistenceManager::logDropCollection(const std::string& collection,
|
|
|
|
|
+ const std::string& originNodeId) {
|
|
|
if (!running_.load()) return;
|
|
if (!running_.load()) return;
|
|
|
- wal_->append(WriteAheadLog::makeDropCollectionEntry(collection));
|
|
|
|
|
|
|
+ const std::string& origin = originNodeId.empty() ? config_.nodeId : originNodeId;
|
|
|
|
|
+ wal_->append(WriteAheadLog::makeDropCollectionEntry(collection, origin));
|
|
|
// Remove collection from tracking
|
|
// Remove collection from tracking
|
|
|
std::lock_guard<std::mutex> lock(collectionSeqMutex_);
|
|
std::lock_guard<std::mutex> lock(collectionSeqMutex_);
|
|
|
collectionSequences_.erase(collection);
|
|
collectionSequences_.erase(collection);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
uint64_t PersistenceManager::logVecPut(const std::string& collection, const std::string& docId,
|
|
uint64_t PersistenceManager::logVecPut(const std::string& collection, const std::string& docId,
|
|
|
- const std::vector<float>& vec) {
|
|
|
|
|
|
|
+ const std::vector<float>& vec,
|
|
|
|
|
+ const std::string& originNodeId) {
|
|
|
if (!running_.load()) return 0;
|
|
if (!running_.load()) return 0;
|
|
|
- auto entry = WriteAheadLog::makeVecPutEntry(collection, docId, vec);
|
|
|
|
|
|
|
+ const std::string& origin = originNodeId.empty() ? config_.nodeId : originNodeId;
|
|
|
|
|
+ auto entry = WriteAheadLog::makeVecPutEntry(collection, docId, vec, origin);
|
|
|
uint64_t seq = wal_->append(entry);
|
|
uint64_t seq = wal_->append(entry);
|
|
|
updateCollectionSequence(collection, seq);
|
|
updateCollectionSequence(collection, seq);
|
|
|
return seq;
|
|
return seq;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-uint64_t PersistenceManager::logVecDelete(const std::string& collection, const std::string& docId) {
|
|
|
|
|
|
|
+uint64_t PersistenceManager::logVecDelete(const std::string& collection, const std::string& docId,
|
|
|
|
|
+ const std::string& originNodeId) {
|
|
|
if (!running_.load()) return 0;
|
|
if (!running_.load()) return 0;
|
|
|
- auto entry = WriteAheadLog::makeVecDeleteEntry(collection, docId);
|
|
|
|
|
|
|
+ const std::string& origin = originNodeId.empty() ? config_.nodeId : originNodeId;
|
|
|
|
|
+ auto entry = WriteAheadLog::makeVecDeleteEntry(collection, docId, origin);
|
|
|
uint64_t seq = wal_->append(entry);
|
|
uint64_t seq = wal_->append(entry);
|
|
|
updateCollectionSequence(collection, seq);
|
|
updateCollectionSequence(collection, seq);
|
|
|
return seq;
|
|
return seq;
|