@@ -2098,6 +2098,88 @@ TEST_P(EPVBucketDurabilityTest, ReplicaToActiveToReplica) {
20982098 }
20992099}
21002100
2101+ /* *
2102+ * Check that converting from Replica to Active back to Replica correctly
2103+ * preserves completed SyncWrites in trackedWrites which cannot yet be removed
2104+ * (if persistMajority and not locally persisted).
2105+ *
2106+ * Scenario:
2107+ * Replica (PassiveDM):
2108+ * 1:prepare(persistToMajority)
2109+ * 2:prepare(majority)
2110+ * 3:commit(1) -> cannot locally remove 1 as not persisted yet.
2111+ * 4:commit(2) -> cannot locally remove 2 as seqno:1 not persisted yet
2112+ * (in-order commit).
2113+ * -> trackedWrites=[1,2]
2114+ * HPS=0
2115+ * HCS=2 (want HCS higher than the first element in the trackedWrites)
2116+ *
2117+ * Convert to ADM (null topology):
2118+ * -> trackedWrites=[1,2]
2119+ * HPS=0
2120+ * HCS=2
2121+ * (i.e. same as previous PassiveDM)
2122+ *
2123+ * Persist seqnos 1:
2124+ * Calls notifyLocalPersistence, but no-op as null topology.
2125+ *
2126+ * Convert to PDM:
2127+ * (seqnos 1 has been persisted previously...)
2128+ * State should be updated to reflect completion of seqno:1:
2129+ * -> trackedWrites=[2]
2130+ * HPS=2
2131+ * HCS=2
2132+ */
2133+ TEST_P (EPVBucketDurabilityTest, ReplicaToActiveToReplica2) {
2134+ // Setup: PassiveDM with
2135+ // 1:PRE(persistMajority), 2:PRE(majority), 3:COMMIT(1), 4:COMMIT(2)
2136+ vbucket->setState (vbucket_state_replica);
2137+ using namespace cb ::durability;
2138+ std::vector<SyncWriteSpec> seqnos{{1 , false , Level::PersistToMajority}, 2 };
2139+ testAddPrepare (seqnos);
2140+ auto & pdm = VBucketTestIntrospector::public_getPassiveDM (*vbucket);
2141+ pdm.completeSyncWrite (makeStoredDocKey (" key1" ),
2142+ PassiveDurabilityMonitor::Resolution::Commit,
2143+ 1 );
2144+ pdm.completeSyncWrite (makeStoredDocKey (" key2" ),
2145+ PassiveDurabilityMonitor::Resolution::Commit,
2146+ 2 );
2147+ pdm.notifySnapshotEndReceived (4 );
2148+
2149+ // Sanity: Check PassiveDM state as expected - HPS is still zero as haven't
2150+ // locally prepared the persistMajority, but globally that's been
2151+ // committed (HCS=1).
2152+ ASSERT_EQ (2 , pdm.getNumTracked ());
2153+ ASSERT_EQ (0 , pdm.getHighPreparedSeqno ());
2154+ ASSERT_EQ (2 , pdm.getHighCompletedSeqno ());
2155+
2156+ // Setup(2): Convert to ActiveDM (null topology).
2157+ vbucket->setState (vbucket_state_active);
2158+ auto & adm = VBucketTestIntrospector::public_getActiveDM (*vbucket);
2159+ EXPECT_EQ (2 , adm.getNumTracked ());
2160+ EXPECT_EQ (0 , adm.getHighPreparedSeqno ());
2161+ EXPECT_EQ (2 , adm.getHighCompletedSeqno ());
2162+
2163+ // Setup(3): Persist seqno 1 (so locally prepared now), but no-op
2164+ // as without topology.
2165+ simulateLocalAck (1 );
2166+ EXPECT_EQ (0 , adm.getNumTracked ());
2167+ EXPECT_EQ (2 , adm.getHighPreparedSeqno ());
2168+ EXPECT_EQ (2 , adm.getHighCompletedSeqno ());
2169+
2170+ // Test: Convert back to PassiveDM. Should remove completed
2171+ // SyncWrites from trackedWrites as have been persisted.
2172+
2173+ // Test: Convert back to PassiveDM.
2174+ vbucket->setState (vbucket_state_replica);
2175+ {
2176+ auto & pdm = VBucketTestIntrospector::public_getPassiveDM (*vbucket);
2177+ EXPECT_EQ (0 , pdm.getNumTracked ());
2178+ EXPECT_EQ (2 , pdm.getHighPreparedSeqno ());
2179+ EXPECT_EQ (2 , pdm.getHighCompletedSeqno ());
2180+ }
2181+ }
2182+
21012183// Test that a double set_vb_state with identical state & topology is handled
21022184// correctly.
21032185// MB-35189: ns_server can send such set_vb_state messages, and in the
0 commit comments