1 // record_store_test_harness.cpp
2 
3 
4 /**
5  *    Copyright (C) 2018-present MongoDB, Inc.
6  *
7  *    This program is free software: you can redistribute it and/or modify
8  *    it under the terms of the Server Side Public License, version 1,
9  *    as published by MongoDB, Inc.
10  *
11  *    This program is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    Server Side Public License for more details.
15  *
16  *    You should have received a copy of the Server Side Public License
17  *    along with this program. If not, see
18  *    <http://www.mongodb.com/licensing/server-side-public-license>.
19  *
20  *    As a special exception, the copyright holders give permission to link the
21  *    code of portions of this program with the OpenSSL library under certain
22  *    conditions as described in each individual source file and distribute
23  *    linked combinations including the program with the OpenSSL library. You
24  *    must comply with the Server Side Public License in all respects for
25  *    all of the code used other than as permitted herein. If you modify file(s)
26  *    with this exception, you may extend this exception to your version of the
27  *    file(s), but you are not obligated to do so. If you do not wish to do so,
28  *    delete this exception statement from your version. If you delete this
29  *    exception statement from all source files in the program, then also delete
30  *    it in the license file.
31  */
32 
33 #include "mongo/platform/basic.h"
34 
35 #include "mongo/db/storage/record_store_test_harness.h"
36 
37 
38 #include "mongo/db/storage/record_store.h"
39 #include "mongo/unittest/unittest.h"
40 
41 namespace mongo {
42 namespace {
43 
44 using std::unique_ptr;
45 using std::string;
46 
TEST(RecordStoreTestHarness,Simple1)47 TEST(RecordStoreTestHarness, Simple1) {
48     const auto harnessHelper(newRecordStoreHarnessHelper());
49     unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
50 
51     {
52         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
53         ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
54     }
55 
56     string s = "eliot was here";
57 
58     RecordId loc1;
59 
60     {
61         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
62         {
63             WriteUnitOfWork uow(opCtx.get());
64             StatusWith<RecordId> res =
65                 rs->insertRecord(opCtx.get(), s.c_str(), s.size() + 1, Timestamp(), false);
66             ASSERT_OK(res.getStatus());
67             loc1 = res.getValue();
68             uow.commit();
69         }
70 
71         ASSERT_EQUALS(s, rs->dataFor(opCtx.get(), loc1).data());
72     }
73 
74     {
75         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
76         ASSERT_EQUALS(s, rs->dataFor(opCtx.get(), loc1).data());
77         ASSERT_EQUALS(1, rs->numRecords(opCtx.get()));
78 
79         RecordData rd;
80         ASSERT(!rs->findRecord(opCtx.get(), RecordId(111, 17), &rd));
81         ASSERT(rd.data() == NULL);
82 
83         ASSERT(rs->findRecord(opCtx.get(), loc1, &rd));
84         ASSERT_EQUALS(s, rd.data());
85     }
86 
87     {
88         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
89         {
90             WriteUnitOfWork uow(opCtx.get());
91             StatusWith<RecordId> res =
92                 rs->insertRecord(opCtx.get(), s.c_str(), s.size() + 1, Timestamp(), false);
93             ASSERT_OK(res.getStatus());
94             uow.commit();
95         }
96     }
97 
98     {
99         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
100         ASSERT_EQUALS(2, rs->numRecords(opCtx.get()));
101     }
102 }
103 
104 namespace {
105 class DummyDocWriter final : public DocWriter {
106 public:
~DummyDocWriter()107     virtual ~DummyDocWriter() {}
108 
writeDocument(char * buf) const109     virtual void writeDocument(char* buf) const {
110         memcpy(buf, "eliot", 6);
111     }
112 
documentSize() const113     virtual size_t documentSize() const {
114         return 6;
115     }
116 
addPadding() const117     virtual bool addPadding() const {
118         return false;
119     }
120 };
121 }
122 
123 
TEST(RecordStoreTestHarness,Simple1InsertDocWroter)124 TEST(RecordStoreTestHarness, Simple1InsertDocWroter) {
125     const auto harnessHelper(newRecordStoreHarnessHelper());
126     unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
127 
128     RecordId loc1;
129 
130     {
131         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
132 
133         {
134             WriteUnitOfWork uow(opCtx.get());
135             DummyDocWriter dw;
136             StatusWith<RecordId> res =
137                 rs->insertRecordWithDocWriter(opCtx.get(), &dw, Timestamp(1));
138             ASSERT_OK(res.getStatus());
139             loc1 = res.getValue();
140             uow.commit();
141         }
142 
143         ASSERT_EQUALS(string("eliot"), rs->dataFor(opCtx.get(), loc1).data());
144     }
145 }
146 
TEST(RecordStoreTestHarness,Delete1)147 TEST(RecordStoreTestHarness, Delete1) {
148     const auto harnessHelper(newRecordStoreHarnessHelper());
149     unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
150 
151     {
152         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
153         ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
154     }
155 
156     string s = "eliot was here";
157 
158     RecordId loc;
159     {
160         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
161 
162         {
163             WriteUnitOfWork uow(opCtx.get());
164             StatusWith<RecordId> res =
165                 rs->insertRecord(opCtx.get(), s.c_str(), s.size() + 1, Timestamp(), false);
166             ASSERT_OK(res.getStatus());
167             loc = res.getValue();
168             uow.commit();
169         }
170 
171         ASSERT_EQUALS(s, rs->dataFor(opCtx.get(), loc).data());
172     }
173 
174     {
175         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
176         ASSERT_EQUALS(1, rs->numRecords(opCtx.get()));
177     }
178 
179     {
180         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
181 
182         {
183             WriteUnitOfWork uow(opCtx.get());
184             rs->deleteRecord(opCtx.get(), loc);
185             uow.commit();
186         }
187 
188         ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
189     }
190 }
191 
TEST(RecordStoreTestHarness,Delete2)192 TEST(RecordStoreTestHarness, Delete2) {
193     const auto harnessHelper(newRecordStoreHarnessHelper());
194     unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
195 
196     {
197         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
198         ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
199     }
200 
201     string s = "eliot was here";
202 
203     RecordId loc;
204     {
205         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
206 
207         {
208             WriteUnitOfWork uow(opCtx.get());
209             StatusWith<RecordId> res =
210                 rs->insertRecord(opCtx.get(), s.c_str(), s.size() + 1, Timestamp(), false);
211             ASSERT_OK(res.getStatus());
212             res = rs->insertRecord(opCtx.get(), s.c_str(), s.size() + 1, Timestamp(), false);
213             ASSERT_OK(res.getStatus());
214             loc = res.getValue();
215             uow.commit();
216         }
217     }
218 
219     {
220         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
221         ASSERT_EQUALS(s, rs->dataFor(opCtx.get(), loc).data());
222         ASSERT_EQUALS(2, rs->numRecords(opCtx.get()));
223     }
224 
225     {
226         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
227         {
228             WriteUnitOfWork uow(opCtx.get());
229             rs->deleteRecord(opCtx.get(), loc);
230             uow.commit();
231         }
232     }
233 }
234 
TEST(RecordStoreTestHarness,Update1)235 TEST(RecordStoreTestHarness, Update1) {
236     const auto harnessHelper(newRecordStoreHarnessHelper());
237     unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
238 
239     {
240         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
241         ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
242     }
243 
244     string s1 = "eliot was here";
245     string s2 = "eliot was here again";
246 
247     RecordId loc;
248     {
249         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
250         {
251             WriteUnitOfWork uow(opCtx.get());
252             StatusWith<RecordId> res =
253                 rs->insertRecord(opCtx.get(), s1.c_str(), s1.size() + 1, Timestamp(), false);
254             ASSERT_OK(res.getStatus());
255             loc = res.getValue();
256             uow.commit();
257         }
258     }
259 
260     {
261         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
262         ASSERT_EQUALS(s1, rs->dataFor(opCtx.get(), loc).data());
263     }
264 
265     {
266         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
267         {
268             WriteUnitOfWork uow(opCtx.get());
269             Status status =
270                 rs->updateRecord(opCtx.get(), loc, s2.c_str(), s2.size() + 1, false, NULL);
271 
272             if (ErrorCodes::NeedsDocumentMove == status) {
273                 // NeedsDocumentMove should only be possible under MMAPv1. We don't have the means
274                 // to check storageEngine here but asserting 'supportsDocLocking()' is false
275                 // provides an equivalent check as only MMAPv1 will/should return false.
276                 ASSERT_FALSE(harnessHelper->supportsDocLocking());
277                 StatusWith<RecordId> newLocation =
278                     rs->insertRecord(opCtx.get(), s2.c_str(), s2.size() + 1, Timestamp(), false);
279                 ASSERT_OK(newLocation.getStatus());
280                 rs->deleteRecord(opCtx.get(), loc);
281                 loc = newLocation.getValue();
282             } else {
283                 ASSERT_OK(status);
284             }
285 
286             uow.commit();
287         }
288     }
289 
290     {
291         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
292         ASSERT_EQUALS(1, rs->numRecords(opCtx.get()));
293         ASSERT_EQUALS(s2, rs->dataFor(opCtx.get(), loc).data());
294     }
295 }
296 
TEST(RecordStoreTestHarness,UpdateInPlace1)297 TEST(RecordStoreTestHarness, UpdateInPlace1) {
298     const auto harnessHelper(newRecordStoreHarnessHelper());
299     unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
300 
301     if (!rs->updateWithDamagesSupported())
302         return;
303 
304     string s1 = "aaa111bbb";
305     string s2 = "aaa222bbb";
306 
307     RecordId loc;
308     const RecordData s1Rec(s1.c_str(), s1.size() + 1);
309     {
310         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
311         {
312             WriteUnitOfWork uow(opCtx.get());
313             StatusWith<RecordId> res =
314                 rs->insertRecord(opCtx.get(), s1Rec.data(), s1Rec.size(), Timestamp(), false);
315             ASSERT_OK(res.getStatus());
316             loc = res.getValue();
317             uow.commit();
318         }
319     }
320 
321     {
322         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
323         ASSERT_EQUALS(s1, rs->dataFor(opCtx.get(), loc).data());
324     }
325 
326     {
327         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
328         {
329             WriteUnitOfWork uow(opCtx.get());
330             const char* damageSource = "222";
331             mutablebson::DamageVector dv;
332             dv.push_back(mutablebson::DamageEvent());
333             dv[0].sourceOffset = 0;
334             dv[0].targetOffset = 3;
335             dv[0].size = 3;
336 
337             auto newRecStatus = rs->updateWithDamages(opCtx.get(), loc, s1Rec, damageSource, dv);
338             ASSERT_OK(newRecStatus.getStatus());
339             ASSERT_EQUALS(s2, newRecStatus.getValue().data());
340             uow.commit();
341         }
342     }
343 
344     {
345         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
346         ASSERT_EQUALS(s2, rs->dataFor(opCtx.get(), loc).data());
347     }
348 }
349 
350 
TEST(RecordStoreTestHarness,Truncate1)351 TEST(RecordStoreTestHarness, Truncate1) {
352     const auto harnessHelper(newRecordStoreHarnessHelper());
353     unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
354 
355     {
356         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
357         ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
358     }
359 
360     string s = "eliot was here";
361 
362     RecordId loc;
363     {
364         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
365         {
366             WriteUnitOfWork uow(opCtx.get());
367             StatusWith<RecordId> res =
368                 rs->insertRecord(opCtx.get(), s.c_str(), s.size() + 1, Timestamp(), false);
369             ASSERT_OK(res.getStatus());
370             loc = res.getValue();
371             uow.commit();
372         }
373     }
374 
375 
376     {
377         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
378         ASSERT_EQUALS(s, rs->dataFor(opCtx.get(), loc).data());
379     }
380 
381     {
382         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
383         ASSERT_EQUALS(1, rs->numRecords(opCtx.get()));
384     }
385 
386     {
387         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
388         {
389             WriteUnitOfWork uow(opCtx.get());
390             rs->truncate(opCtx.get()).transitional_ignore();
391             uow.commit();
392         }
393     }
394 
395     {
396         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
397         ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
398     }
399 }
400 
TEST(RecordStoreTestHarness,Cursor1)401 TEST(RecordStoreTestHarness, Cursor1) {
402     const int N = 10;
403 
404     const auto harnessHelper(newRecordStoreHarnessHelper());
405     unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
406 
407     {
408         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
409         ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
410     }
411 
412     {
413         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
414         {
415             WriteUnitOfWork uow(opCtx.get());
416             for (int i = 0; i < N; i++) {
417                 string s = str::stream() << "eliot" << i;
418                 ASSERT_OK(rs->insertRecord(opCtx.get(), s.c_str(), s.size() + 1, Timestamp(), false)
419                               .getStatus());
420             }
421             uow.commit();
422         }
423     }
424 
425     {
426         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
427         ASSERT_EQUALS(N, rs->numRecords(opCtx.get()));
428     }
429 
430     {
431         int x = 0;
432         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
433         auto cursor = rs->getCursor(opCtx.get());
434         while (auto record = cursor->next()) {
435             string s = str::stream() << "eliot" << x++;
436             ASSERT_EQUALS(s, record->data.data());
437         }
438         ASSERT_EQUALS(N, x);
439         ASSERT(!cursor->next());
440     }
441 
442     {
443         int x = N;
444         ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
445         auto cursor = rs->getCursor(opCtx.get(), false);
446         while (auto record = cursor->next()) {
447             string s = str::stream() << "eliot" << --x;
448             ASSERT_EQUALS(s, record->data.data());
449         }
450         ASSERT_EQUALS(0, x);
451         ASSERT(!cursor->next());
452     }
453 }
454 }  // namespace
455 }  // namespace mongo
456