1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2018 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #pragma once
19 
20 #include "vbucket_test.h"
21 
22 class MockCheckpointManager;
23 class ActiveDurabilityMonitor;
24 
25 /*
26  * VBucket unit tests related to durability.
27  */
28 class VBucketDurabilityTest : public VBucketTest {
29 public:
30     void SetUp();
31 
32 protected:
33     /// Specification of a SyncWrite to store, as used by storeSyncWrites.
34     struct SyncWriteSpec {
SyncWriteSpecVBucketDurabilityTest::SyncWriteSpec35         explicit SyncWriteSpec(int64_t seqno) : seqno(seqno) {
36         }
SyncWriteSpecVBucketDurabilityTest::SyncWriteSpec37         SyncWriteSpec(int64_t seqno, bool deletion = false)
38             : seqno(seqno), deletion(deletion) {
39         }
SyncWriteSpecVBucketDurabilityTest::SyncWriteSpec40         SyncWriteSpec(int64_t seqno,
41                       bool deletion,
42                       cb::durability::Level level,
43                       cb::durability::Timeout timeout =
44                               cb::durability::Timeout::Infinity())
45             : seqno(seqno), deletion(deletion), level(level), timeout(timeout) {
46         }
47 
48         int64_t seqno;
49         bool deletion = false;
50         cb::durability::Level level = cb::durability::Level::Majority;
51         cb::durability::Timeout timeout = cb::durability::Timeout::Infinity();
52     };
53 
54     /**
55      * Store the given Sync mutations into VBucket
56      *
57      * @param writes the mutations to be added
58      */
59     void storeSyncWrites(const std::vector<SyncWriteSpec>& writes);
60 
61     /**
62      * Simulate a SetVBucketState call, including processing any resolved
63      * SyncWrites which can now be Committed/Aborted.
64      */
65     void simulateSetVBState(vbucket_state_t to,
66                             const nlohmann::json& meta = {});
67 
68     /**
69      * Simulate the local (active) seqno acknowledgement.
70      *
71      * @param seqno The ack'ed seqno
72      */
73     void simulateLocalAck(uint64_t seqno);
74 
75     /**
76      * Tests:
77      * 1) mutations added to VBucket
78      * 2) mutations in state "pending" in both HashTable and CheckpointManager
79      *
80      * @param writes the set of mutations to test
81      */
82     void testAddPrepare(const std::vector<SyncWriteSpec>& writes);
83 
84     /**
85      * Tests the baseline progress of a set of SyncWrites in Vbucket:
86      * 1) mutations added to VBucket
87      * 2) mutations in state "pending" in both HashTable and CheckpointManager
88      * 3) VBucket receives a SeqnoAck that satisfies the DurReqs for all SWs
89      * 4) mutations in state "committed" in both HashTable and CheckpointManager
90      *
91      * @param writes the set of mutations to test
92      */
93     void testAddPrepareAndCommit(const std::vector<SyncWriteSpec>& writes);
94 
95     /**
96      * Tests that the Replication Topology is cleared when a VBucket transitions
97      * to the given state.
98      *
99      * @param state The new state for VBucket
100      */
101     void testSetVBucketState_ClearTopology(vbucket_state_t state);
102 
103     /**
104      * Tests that the PassiveDM queues incoming Prepares correctly when the
105      * owning VBucket is in the given state.
106      *
107      * @param state The state for VBucket
108      * @param writes The Prepares to be queued
109      */
110     void testAddPrepareInPassiveDM(vbucket_state_t state,
111                                    const std::vector<SyncWriteSpec>& writes);
112 
113     /**
114      * Tests that the PassiveDM is correctly converted to ActiveDM when a
115      * VBucket in the provided initial state transitions to vbstate-active when
116      * there are in-flight SyncWrites.
117      *
118      * @param initialState The initial state for VBucket
119      */
120     void testConvertPassiveDMToActiveDM(vbucket_state_t initialState);
121 
122     /**
123      * Tests that the PassiveDM is correctly converted to ActiveDM when a
124      * vBucket in the provided initial state transitions to vbstate-active when
125      * there are in-flight SyncWrites. This test mimics an actual takeover which
126      * will do the following set of state transitions:
127      * replica/pending->active with no topology->active with topology. Persists
128      * up to seqno 2 regardless of the writes passed in.
129      *
130      * @param initialState The initial state for the vBucket
131      * @param writes The prepares to be queued
132      */
133     void testConvertPDMToADMWithNullTopologySetup(
134             vbucket_state_t initialState, std::vector<SyncWriteSpec>& writes);
135 
136     /**
137      * Tests that the PassiveDM is correctly converted to ActiveDM when a
138      * vBucket in the provided initial state transitions to vbstate-active when
139      * there are in-flight SyncWrites. This test mimics an actual takeover which
140      * will do the following set of state transitions:
141      * replica/pending->active with no topology->active with topology
142      *
143      * @param initialState The initial state for the vBucket
144      */
145     void testConvertPDMToADMWithNullTopology(vbucket_state_t initialState);
146 
147     /**
148      * Tests that the PassiveDM is correctly converted to ActiveDM when a
149      * vBucket in the provided initial state transitions to vbstate-active when
150      * there are in-flight SyncWrites. This test mimics an actual takeover which
151      * will do the following set of state transitions:
152      * replica/pending->active with no topology->active with topology. This test
153      * tests that a PersistToMajority Prepare persisted after the final topology
154      * change is done correctly.
155      *
156      * @param initialState The initial state for the vBucket
157      */
158     void testConvertPDMToADMWithNullTopologyPersistAfterTopologyChange(
159             vbucket_state_t initialState);
160 
161     /**
162      * Tests that the PassiveDM is correctly converted to ActiveDM when a
163      * vBucket in the provided initial state transitions to vbstate-active when
164      * there are in-flight SyncWrites. This test mimics an actual takeover which
165      * will do the following set of state transitions:
166      * replica/pending->active with no topology->active with topology. This test
167      * tests that a PersistToMajority Prepare persisted between the null and
168      * final topology change is done correctly.
169      *
170      * @param initialState The initial state for the vBucket
171      */
172     void testConvertPDMToADMWithNullTopologyPersistBeforeTopologyChange(
173             vbucket_state_t initialState);
174 
175     /**
176      * Tests that the PassiveDM is correctly converted to ActiveDM when a
177      * vBucket in the provided initial state transitions to vbstate-active when
178      * there are in-flight SyncWrites. This test mimics an actual takeover which
179      * will do the following set of state transitions:
180      * replica/pending->active with no topology->active with topology. This test
181      * tests that a HPS that does not equal anything in trackedWrites is moved
182      * over the the ADM and that a subsequent prepare and commit can be
183      * performed successfully.
184      *
185      * @param initialState The initial state for the vBucket
186      */
187     void testConvertPDMToADMWithNullTopologyPostDiskSnap(
188             vbucket_state_t initialState);
189 
190     /**
191      * Tests that the PassiveDM is correctly converted to ActiveDM when a
192      * vBucket in the provided initial state transitions to vbstate-active when
193      * there is an in-flight PersistToMajority SyncWrite that has not yet been
194      * persisted.
195      *
196      * @param initialState The initial state for the vBucket
197      */
198     void testConvertPassiveDMToActiveDMUnpersistedPrepare(
199             vbucket_state_t initialState);
200 
201     /**
202      * Tests that the PassiveDM is correctly converted to ActiveDM when a
203      * vBucket in the provided initial state transitions to vbstate-active in
204      * the middle of a snapshot.
205      *
206      * @param initialState The initial state for the vBucket
207      */
208     void testConvertPDMToADMMidSnapSetup(vbucket_state_t initialState);
209 
210     /**
211      * Tests that the PassiveDM is correctly converted to ActiveDM when a
212      * vBucket in the provided initial state transitions to vbstate-active in
213      * the middle of a snapshot. Persists items before the topology change so
214      * that the topology change drives the commit.
215      *
216      * @param initialState The initial state for the vBucket
217      */
218     void testConvertPDMToADMMidSnapSetupPersistBeforeChange(
219             vbucket_state_t initialState);
220 
221     /**
222      * Tests that the PassiveDM is correctly converted to ActiveDM when a
223      * vBucket in the provided initial state transitions to vbstate-active in
224      * the middle of a snapshot. Persists items after the topology change so
225      * that a call to ADM::checkForCommit drives the commit.
226      *
227      * @param initialState The initial state for the vBucket
228      */
229     void testConvertPDMToADMMidSnapSetupPersistAfterChange(
230             vbucket_state_t initialState);
231 
232     /**
233      * Tests that the PassiveDM is correctly converted to ActiveDM when a
234      * vBucket in the provided initial state transitions to vbstate-active in
235      * the middle of a snapshot. Tests that the topology change is successful
236      * when we have completed all prepares in the PDM.
237      *
238      * @param initialState The initial state for the vBucket
239      */
240     void testConvertPDMToADMMidSnapAllPreparesCompleted(
241             vbucket_state_t initialState);
242 
243     /**
244      * Test that the PassiveDM is correctly converted to ActiveDM when a
245      * VBucket in the provided initial state transitions to vbstate-active when
246      * there are no in-flight SyncWrites.
247      *
248      * @param initialState The initial state for VBucket
249      */
250     void testConvertPassiveDMToActiveDMNoPrepares(vbucket_state_t initialState);
251 
252     /**
253      * Test that the state transition from active does or does not converts
254      * the prepares in the HashTable to PreparedMaybeVisible.
255      *
256      * @param toState The state to convert the vBucket to
257      * @param expectPreparedMaybeVisible should the conversion happen
258      */
259     void testConvertADMMakesPreparesMaybeVisible(
260             vbucket_state_t toState, bool expectPreparedMaybeVisible);
261 
262     enum class Resolution { Commit, Abort };
263 
264     /**
265      * Tests that the PassiveDM behaves correctly when VBucket notifies the
266      * PassiveDM of Prepare completion (ie, Commit or Abort).
267      *
268      * @param initialState The initial state for VBucket
269      * @param res The type of resolution, Commit/Abort
270      */
271     void testCompleteSWInPassiveDM(vbucket_state_t initialState,
272                                    Resolution res);
273 
274     /**
275      * Test a normal set followed by a pending SyncWrite; then committing the
276      * pending SyncWrite which should replace the previous committed.
277      */
278     void testHTCommitExisting();
279 
280     /**
281      * Add a pending SyncDelete to the vBucket for the given key.
282      */
283     void setupPendingDelete(StoredDocKey key);
284 
285     /**
286      * Test a commit of a sync delete
287      */
288     void testHTSyncDeleteCommit();
289 
290     void doSyncWriteAndCommit();
291     void doSyncDelete();
292 
293     MutationStatus doPrepareSyncSet(const StoredDocKey& key, std::string value);
294     AddStatus doPrepareSyncAdd(const StoredDocKey& key, std::string value);
295 
296     // All owned by VBucket
297     HashTable* ht;
298     MockCheckpointManager* ckptMgr;
299 
300     const std::string active = "active";
301     const std::string replica1 = "replica1";
302     const std::string replica2 = "replica2";
303     const std::string replica3 = "replica3";
304 };
305 
306 class EPVBucketDurabilityTest : public VBucketDurabilityTest {};
307 class EphemeralVBucketDurabilityTest : public VBucketDurabilityTest {};
308