summaryrefslogtreecommitdiff
path: root/src/mongo/db/storage/record_store_test_randomiter.cpp
diff options
context:
space:
mode:
authorGeert Bosch <geert@mongodb.com>2015-07-31 21:03:18 -0400
committerGeert Bosch <geert@mongodb.com>2015-08-07 23:32:15 -0400
commitd7eb8b1f43dfa70cad97168a182ffdc01eaa1aeb (patch)
treea6db1f10d1e7fea078dd776e9069e88443e19eb0 /src/mongo/db/storage/record_store_test_randomiter.cpp
parent61b9c8d1ca4a28a10d81c0da89425b74e71e0d11 (diff)
downloadmongo-d7eb8b1f43dfa70cad97168a182ffdc01eaa1aeb.tar.gz
SERVER-19183: Allow storage engines to provide optimized random cursors for use by $sample
Diffstat (limited to 'src/mongo/db/storage/record_store_test_randomiter.cpp')
-rw-r--r--src/mongo/db/storage/record_store_test_randomiter.cpp182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/mongo/db/storage/record_store_test_randomiter.cpp b/src/mongo/db/storage/record_store_test_randomiter.cpp
new file mode 100644
index 00000000000..a5921890a03
--- /dev/null
+++ b/src/mongo/db/storage/record_store_test_randomiter.cpp
@@ -0,0 +1,182 @@
+// record_store_test_randomiter.cpp
+
+/**
+ * Copyright (C) 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/storage/record_store_test_harness.h"
+
+
+#include "mongo/db/record_id.h"
+#include "mongo/db/storage/record_data.h"
+#include "mongo/db/storage/record_store.h"
+#include "mongo/unittest/unittest.h"
+
+using std::unique_ptr;
+using std::set;
+using std::string;
+using std::stringstream;
+
+namespace mongo {
+
+// Create a random iterator for empty record store.
+TEST(RecordStoreTestHarness, GetRandomIteratorEmpty) {
+ unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper());
+ unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
+
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
+ }
+
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ auto cursor = rs->getRandomCursor(opCtx.get());
+ // returns NULL if getRandomCursor is not supported
+ if (!cursor) {
+ return;
+ }
+ ASSERT(!cursor->next());
+ }
+}
+
+// Insert multiple records and create a random iterator for the record store
+TEST(RecordStoreTestHarness, GetRandomIteratorNonEmpty) {
+ unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper());
+ unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
+
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
+ }
+
+ const unsigned nToInsert =
+ 5000; // should be non-trivial amount, so we get multiple btree levels
+ RecordId locs[nToInsert];
+ for (unsigned i = 0; i < nToInsert; i++) {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ {
+ stringstream ss;
+ ss << "record " << i;
+ string data = ss.str();
+
+ WriteUnitOfWork uow(opCtx.get());
+ StatusWith<RecordId> res =
+ rs->insertRecord(opCtx.get(), data.c_str(), data.size() + 1, false);
+ ASSERT_OK(res.getStatus());
+ locs[i] = res.getValue();
+ uow.commit();
+ }
+ }
+
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ ASSERT_EQUALS(nToInsert, rs->numRecords(opCtx.get()));
+ }
+
+ set<RecordId> remain(locs, locs + nToInsert);
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ auto cursor = rs->getRandomCursor(opCtx.get());
+ // returns NULL if getRandomCursor is not supported
+ if (!cursor) {
+ return;
+ }
+
+ // Iterate documents and mark those visited, but let at least one remain
+ for (unsigned i = 0; i < nToInsert - 1; i++) {
+ // Get a new cursor once in a while, shouldn't affect things
+ if (i % (nToInsert / 8) == 0) {
+ cursor = rs->getRandomCursor(opCtx.get());
+ }
+ remain.erase(cursor->next()->id); // can happen more than once per doc
+ }
+ ASSERT(!remain.empty());
+ ASSERT(cursor->next());
+
+ // We should have at least visited a quarter of the items if we're any random at all
+ // This should be enabled once the fix for WT-2032 is integrated:
+ // ASSERT_LT(remain.size(), nToInsert * 3 / 4);
+ }
+}
+
+// Insert a single record. Create a random iterator pointing to that single record.
+// Then check we'll retrieve the record.
+TEST(RecordStoreTestHarness, GetRandomIteratorSingleton) {
+ unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper());
+ unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
+
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ ASSERT_EQ(0, rs->numRecords(opCtx.get()));
+ }
+
+ // Insert one record.
+ RecordId idToRetrieve;
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ WriteUnitOfWork uow(opCtx.get());
+ StatusWith<RecordId> res = rs->insertRecord(opCtx.get(), "some data", 10, false);
+ ASSERT_OK(res.getStatus());
+ idToRetrieve = res.getValue();
+ uow.commit();
+ }
+
+ // Double-check that the record store has one record in it now.
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ ASSERT_EQ(1, rs->numRecords(opCtx.get()));
+ }
+
+ {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ auto cursor = rs->getRandomCursor(opCtx.get());
+ // returns NULL if getRandomCursor is not supported
+ if (!cursor) {
+ return;
+ }
+
+ // We should be pointing at the only record in the store.
+
+ // Check deattaching / reattaching
+ cursor->savePositioned();
+ cursor->detachFromOperationContext();
+ opCtx = harnessHelper->newOperationContext();
+ cursor->reattachToOperationContext(opCtx.get());
+ cursor->restore();
+
+ auto record = cursor->next();
+ ASSERT_EQUALS(record->id, idToRetrieve);
+
+ // Iterator should either be EOF now, or keep returning the single existing document
+ for (int i = 0; i < 10; i++) {
+ record = cursor->next();
+ ASSERT(!record || record->id == idToRetrieve);
+ }
+ }
+}
+} // namespace mongo