From 852be7b4dde4490c02ff780a45013ad2a8cd2c1e Mon Sep 17 00:00:00 2001 From: shangruqi Date: Wed, 25 Nov 2020 13:10:34 +0800 Subject: [PATCH 1/2] An workaround for conflict resolve failure --- LiteCore/RevTrees/RevTree.cc | 22 ++++++++++++++++++++++ LiteCore/RevTrees/RevTree.hh | 9 +++++++++ LiteCore/RevTrees/VersionedDocument.cc | 1 + 3 files changed, 32 insertions(+) diff --git a/LiteCore/RevTrees/RevTree.cc b/LiteCore/RevTrees/RevTree.cc index 621d16fd9..8ed2ce306 100644 --- a/LiteCore/RevTrees/RevTree.cc +++ b/LiteCore/RevTrees/RevTree.cc @@ -606,6 +606,28 @@ void RevTree::resetConflictSequence(const Rev* winningRev) { return false; } + bool RevTree::ensureConflictStateConsistent() { + if (hasConflict()) { + sort(); + bool recovered = false; + // _revs[0] is current rev after sort + for (auto rev = _revs.begin() + 1; rev != _revs.end(); rev++) { + auto f = (*rev)->flags; + // a undeleted, non-closed, unconflict leaf rev + if ((f & Rev::kLeaf) && + !(f & Rev::kClosed) && + !(f & Rev::kDeleted) && + !(f & Rev::kIsConflict)) { + (*rev)->addFlag(Rev::kIsConflict); + recovered = true; + } + } + _changed |= recovered; + return recovered; + } + return false; + } + void RevTree::saved(sequence_t newSequence) { for (Rev *rev : _revs) { rev->clearFlag(Rev::kNew); diff --git a/LiteCore/RevTrees/RevTree.hh b/LiteCore/RevTrees/RevTree.hh index d3bc5b87c..b59c1404f 100644 --- a/LiteCore/RevTrees/RevTree.hh +++ b/LiteCore/RevTrees/RevTree.hh @@ -112,6 +112,15 @@ namespace litecore { bool hasConflict() const; bool hasNewRevisions() const; + /// For unknown reason, sometimes the rev tree would come into an inconsistent state, + /// in which some conflicted leaf revs are not tagged conflict. + /// The method is a workaround for the problem. It tag those unclosed, non-current + /// leaves as conflict, solving the problem regardless of its real cause. + /// + /// Return true if it was recovered from inconsistent state + /// Return false otherwise + bool ensureConflictStateConsistent(); + /// Given an array of revision IDs in consecutive descending-generation order, /// finds the first one that exists in this tree. Returns: /// * {rev, index} if a common ancestor was found; diff --git a/LiteCore/RevTrees/VersionedDocument.cc b/LiteCore/RevTrees/VersionedDocument.cc index a060eec23..0dc82459f 100644 --- a/LiteCore/RevTrees/VersionedDocument.cc +++ b/LiteCore/RevTrees/VersionedDocument.cc @@ -164,6 +164,7 @@ namespace litecore { } VersionedDocument::SaveResult VersionedDocument::save(Transaction& transaction) { + ensureConflictStateConsistent(); if (!_changed) return kNoNewSequence; updateMeta(); From 83fce7b52e9768a0cee1537c4a7992f7310af5f9 Mon Sep 17 00:00:00 2001 From: shangruqi Date: Wed, 25 Nov 2020 15:28:33 +0800 Subject: [PATCH 2/2] Add some logging about conflict state recover for debugging --- LiteCore/RevTrees/VersionedDocument.cc | 4 +++- LiteCore/RevTrees/VersionedDocument.hh | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/LiteCore/RevTrees/VersionedDocument.cc b/LiteCore/RevTrees/VersionedDocument.cc index 0dc82459f..459926034 100644 --- a/LiteCore/RevTrees/VersionedDocument.cc +++ b/LiteCore/RevTrees/VersionedDocument.cc @@ -164,7 +164,9 @@ namespace litecore { } VersionedDocument::SaveResult VersionedDocument::save(Transaction& transaction) { - ensureConflictStateConsistent(); + if (_usuallyFalse(ensureConflictStateConsistent())) { + Warn("Document '%.*s' recovered from inconsistent state", SPLAT(docID())); + } if (!_changed) return kNoNewSequence; updateMeta(); diff --git a/LiteCore/RevTrees/VersionedDocument.hh b/LiteCore/RevTrees/VersionedDocument.hh index 3694408a6..4b5d28890 100644 --- a/LiteCore/RevTrees/VersionedDocument.hh +++ b/LiteCore/RevTrees/VersionedDocument.hh @@ -20,6 +20,8 @@ #include "RevTree.hh" #include "Record.hh" #include "Doc.hh" +#include "Logging.hh" +#include "StringUtil.hh" #include #include