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