diff options
Diffstat (limited to 'src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp')
-rw-r--r-- | src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp | 843 |
1 files changed, 422 insertions, 421 deletions
diff --git a/src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp b/src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp index aa1b2adfe00..679bb3f8c8b 100644 --- a/src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp +++ b/src/mongo/db/storage/sorted_data_interface_test_cursor_saverestore.cpp @@ -37,480 +37,481 @@ namespace mongo { - // Insert multiple keys and try to iterate through all of them - // using a forward cursor while calling savePosition() and - // restorePosition() in succession. - TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursor ) { - const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); - const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) ); - - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT( sorted->isEmpty( opCtx.get() ) ); - } - - int nToInsert = 10; - for ( int i = 0; i < nToInsert; i++ ) { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - { - WriteUnitOfWork uow( opCtx.get() ); - BSONObj key = BSON( "" << i ); - RecordId loc( 42, i * 2 ); - ASSERT_OK( sorted->insert( opCtx.get(), key, loc, true ) ); - uow.commit(); - } - } - - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) ); - } - - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) ); - int i = 0; - for (auto entry = cursor->seek(minKey, true); entry; i++, entry = cursor->next()) { - ASSERT_LT(i, nToInsert); - ASSERT_EQ(entry, IndexKeyEntry(BSON( "" << i), RecordId(42, i * 2))); - - cursor->savePositioned(); - cursor->restore( opCtx.get() ); - } - ASSERT( !cursor->next() ); - ASSERT_EQ(i, nToInsert); - } +// Insert multiple keys and try to iterate through all of them +// using a forward cursor while calling savePosition() and +// restorePosition() in succession. +TEST(SortedDataInterface, SaveAndRestorePositionWhileIterateCursor) { + const std::unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper()); + const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false)); + + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT(sorted->isEmpty(opCtx.get())); } - // Insert multiple keys and try to iterate through all of them - // using a reverse cursor while calling savePosition() and - // restorePosition() in succession. - TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursorReversed ) { - const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); - const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) ); - + int nToInsert = 10; + for (int i = 0; i < nToInsert; i++) { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT( sorted->isEmpty( opCtx.get() ) ); + WriteUnitOfWork uow(opCtx.get()); + BSONObj key = BSON("" << i); + RecordId loc(42, i * 2); + ASSERT_OK(sorted->insert(opCtx.get(), key, loc, true)); + uow.commit(); } + } - int nToInsert = 10; - for ( int i = 0; i < nToInsert; i++ ) { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - { - WriteUnitOfWork uow( opCtx.get() ); - BSONObj key = BSON( "" << i ); - RecordId loc( 42, i * 2 ); - ASSERT_OK( sorted->insert( opCtx.get(), key, loc, true ) ); - uow.commit(); - } - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT_EQUALS(nToInsert, sorted->numEntries(opCtx.get())); + } - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) ); - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get())); + int i = 0; + for (auto entry = cursor->seek(minKey, true); entry; i++, entry = cursor->next()) { + ASSERT_LT(i, nToInsert); + ASSERT_EQ(entry, IndexKeyEntry(BSON("" << i), RecordId(42, i * 2))); - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) ); - int i = nToInsert - 1; - for (auto entry = cursor->seek(maxKey, true); entry; i--, entry = cursor->next()) { - ASSERT_GTE(i, 0); - ASSERT_EQ(entry, IndexKeyEntry(BSON( "" << i), RecordId(42, i * 2))); - - cursor->savePositioned(); - cursor->restore( opCtx.get() ); - } - ASSERT( !cursor->next() ); - ASSERT_EQ(i, -1); + cursor->savePositioned(); + cursor->restore(opCtx.get()); } + ASSERT(!cursor->next()); + ASSERT_EQ(i, nToInsert); + } +} + +// Insert multiple keys and try to iterate through all of them +// using a reverse cursor while calling savePosition() and +// restorePosition() in succession. +TEST(SortedDataInterface, SaveAndRestorePositionWhileIterateCursorReversed) { + const std::unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper()); + const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false)); + + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT(sorted->isEmpty(opCtx.get())); } - // Insert the same key multiple times and try to iterate through each - // occurrence using a forward cursor while calling savePosition() and - // restorePosition() in succession. Verify that the RecordId is saved - // as part of the current position of the cursor. - TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeys ) { - const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); - const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) ); - + int nToInsert = 10; + for (int i = 0; i < nToInsert; i++) { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT( sorted->isEmpty( opCtx.get() ) ); + WriteUnitOfWork uow(opCtx.get()); + BSONObj key = BSON("" << i); + RecordId loc(42, i * 2); + ASSERT_OK(sorted->insert(opCtx.get(), key, loc, true)); + uow.commit(); } + } - int nToInsert = 10; - for ( int i = 0; i < nToInsert; i++ ) { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - { - WriteUnitOfWork uow( opCtx.get() ); - RecordId loc( 42, i * 2 ); - ASSERT_OK( sorted->insert( opCtx.get(), key1, loc, true /* allow duplicates */ ) ); - uow.commit(); - } - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT_EQUALS(nToInsert, sorted->numEntries(opCtx.get())); + } - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) ); - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + const std::unique_ptr<SortedDataInterface::Cursor> cursor( + sorted->newCursor(opCtx.get(), false)); + int i = nToInsert - 1; + for (auto entry = cursor->seek(maxKey, true); entry; i--, entry = cursor->next()) { + ASSERT_GTE(i, 0); + ASSERT_EQ(entry, IndexKeyEntry(BSON("" << i), RecordId(42, i * 2))); - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) ); - int i = 0; - for (auto entry = cursor->seek(minKey, true); entry; i++, entry = cursor->next()) { - ASSERT_LT(i, nToInsert); - ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2))); - - cursor->savePositioned(); - cursor->restore( opCtx.get() ); - } - ASSERT( !cursor->next() ); - ASSERT_EQ(i, nToInsert); + cursor->savePositioned(); + cursor->restore(opCtx.get()); } + ASSERT(!cursor->next()); + ASSERT_EQ(i, -1); + } +} + +// Insert the same key multiple times and try to iterate through each +// occurrence using a forward cursor while calling savePosition() and +// restorePosition() in succession. Verify that the RecordId is saved +// as part of the current position of the cursor. +TEST(SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeys) { + const std::unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper()); + const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false)); + + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT(sorted->isEmpty(opCtx.get())); } - // Insert the same key multiple times and try to iterate through each - // occurrence using a reverse cursor while calling savePosition() and - // restorePosition() in succession. Verify that the RecordId is saved - // as part of the current position of the cursor. - TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeysReversed ) { - const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); - const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) ); - + int nToInsert = 10; + for (int i = 0; i < nToInsert; i++) { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT( sorted->isEmpty( opCtx.get() ) ); + WriteUnitOfWork uow(opCtx.get()); + RecordId loc(42, i * 2); + ASSERT_OK(sorted->insert(opCtx.get(), key1, loc, true /* allow duplicates */)); + uow.commit(); } + } - int nToInsert = 10; - for ( int i = 0; i < nToInsert; i++ ) { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - { - WriteUnitOfWork uow( opCtx.get() ); - RecordId loc( 42, i * 2 ); - ASSERT_OK( sorted->insert( opCtx.get(), key1, loc, true /* allow duplicates */ ) ); - uow.commit(); - } - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT_EQUALS(nToInsert, sorted->numEntries(opCtx.get())); + } - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) ); - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get())); + int i = 0; + for (auto entry = cursor->seek(minKey, true); entry; i++, entry = cursor->next()) { + ASSERT_LT(i, nToInsert); + ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2))); - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) ); - int i = nToInsert - 1; - for (auto entry = cursor->seek(maxKey, true); entry; i--, entry = cursor->next()) { - ASSERT_GTE(i, 0); - ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2))); - - cursor->savePositioned(); - cursor->restore( opCtx.get() ); - } - ASSERT( !cursor->next() ); - ASSERT_EQ(i, -1); + cursor->savePositioned(); + cursor->restore(opCtx.get()); } + ASSERT(!cursor->next()); + ASSERT_EQ(i, nToInsert); + } +} + +// Insert the same key multiple times and try to iterate through each +// occurrence using a reverse cursor while calling savePosition() and +// restorePosition() in succession. Verify that the RecordId is saved +// as part of the current position of the cursor. +TEST(SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeysReversed) { + const std::unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper()); + const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false)); + + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT(sorted->isEmpty(opCtx.get())); } - // Call savePosition() on a forward cursor without ever calling restorePosition(). - // May be useful to run this test under valgrind to verify there are no leaks. - TEST( SortedDataInterface, SavePositionWithoutRestore ) { - const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); - const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( true ) ); - + int nToInsert = 10; + for (int i = 0; i < nToInsert; i++) { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT( sorted->isEmpty( opCtx.get() ) ); + WriteUnitOfWork uow(opCtx.get()); + RecordId loc(42, i * 2); + ASSERT_OK(sorted->insert(opCtx.get(), key1, loc, true /* allow duplicates */)); + uow.commit(); } + } - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - { - WriteUnitOfWork uow( opCtx.get() ); - ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, false ) ); - uow.commit(); - } - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT_EQUALS(nToInsert, sorted->numEntries(opCtx.get())); + } - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) ); - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + const std::unique_ptr<SortedDataInterface::Cursor> cursor( + sorted->newCursor(opCtx.get(), false)); + int i = nToInsert - 1; + for (auto entry = cursor->seek(maxKey, true); entry; i--, entry = cursor->next()) { + ASSERT_GTE(i, 0); + ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2))); - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) ); cursor->savePositioned(); + cursor->restore(opCtx.get()); } + ASSERT(!cursor->next()); + ASSERT_EQ(i, -1); } +} - // Call savePosition() on a reverse cursor without ever calling restorePosition(). - // May be useful to run this test under valgrind to verify there are no leaks. - TEST( SortedDataInterface, SavePositionWithoutRestoreReversed ) { - const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); - const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) ); - - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT( sorted->isEmpty( opCtx.get() ) ); - } - - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - { - WriteUnitOfWork uow( opCtx.get() ); - ASSERT_OK( sorted->insert( opCtx.get(), key1, loc1, true ) ); - uow.commit(); - } - } +// Call savePosition() on a forward cursor without ever calling restorePosition(). +// May be useful to run this test under valgrind to verify there are no leaks. +TEST(SortedDataInterface, SavePositionWithoutRestore) { + const std::unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper()); + const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(true)); - { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - ASSERT_EQUALS( 1, sorted->numEntries( opCtx.get() ) ); - } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT(sorted->isEmpty(opCtx.get())); + } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); { - const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); - const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) ); - cursor->savePositioned(); + WriteUnitOfWork uow(opCtx.get()); + ASSERT_OK(sorted->insert(opCtx.get(), key1, loc1, false)); + uow.commit(); } } - - // Ensure that restore lands as close as possible to original position, even if data inserted - // while saved. - void testSaveAndRestorePositionSeesNewInserts(bool forward, bool unique) { - auto harnessHelper = newHarnessHelper(); - auto opCtx = harnessHelper->newOperationContext(); - auto sorted = harnessHelper->newSortedDataInterface(unique, { - {key1, loc1}, - {key3, loc1}, - }); - - auto cursor = sorted->newCursor(opCtx.get(), forward); - const auto seekPoint = forward ? key1 : key3; - - ASSERT_EQ(cursor->seek(seekPoint, true), IndexKeyEntry(seekPoint, loc1)); - cursor->savePositioned(); - insertToIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); - - ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInserts_Forward_Unique) { - testSaveAndRestorePositionSeesNewInserts(true, true); + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT_EQUALS(1, sorted->numEntries(opCtx.get())); } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInserts_Forward_Standard) { - testSaveAndRestorePositionSeesNewInserts(true, false); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInserts_Reverse_Unique) { - testSaveAndRestorePositionSeesNewInserts(false, true); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInserts_Reverse_Standard) { - testSaveAndRestorePositionSeesNewInserts(false, false); - } - - // Ensure that repeated restores lands as close as possible to original position, even if data - // inserted while saved and the current position removed. - void testSaveAndRestorePositionSeesNewInsertsAfterRemove(bool forward, bool unique) { - auto harnessHelper = newHarnessHelper(); - auto opCtx = harnessHelper->newOperationContext(); - auto sorted = harnessHelper->newSortedDataInterface(unique, { - {key1, loc1}, - {key3, loc1}, - }); - - auto cursor = sorted->newCursor(opCtx.get(), forward); - const auto seekPoint = forward ? key1 : key3; - - ASSERT_EQ(cursor->seek(seekPoint, true), IndexKeyEntry(seekPoint, loc1)); + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get())); cursor->savePositioned(); - removeFromIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); - // The restore may have seeked since it can't return to the saved position. - - cursor->savePositioned(); // Should still save originally saved key as "current position". - insertToIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); - - ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterRemove_Forward_Unique) { - testSaveAndRestorePositionSeesNewInsertsAfterRemove(true, true); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterRemove_Forward_Standard) { - testSaveAndRestorePositionSeesNewInsertsAfterRemove(true, false); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterRemove_Reverse_Unique) { - testSaveAndRestorePositionSeesNewInsertsAfterRemove(false, true); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterRemove_Reverse_Standard) { - testSaveAndRestorePositionSeesNewInsertsAfterRemove(false, false); } +} - // Ensure that repeated restores lands as close as possible to original position, even if data - // inserted while saved and the current position removed in a way that temporarily makes the - // cursor EOF. - void testSaveAndRestorePositionSeesNewInsertsAfterEOF(bool forward, bool unique) { - auto harnessHelper = newHarnessHelper(); - auto opCtx = harnessHelper->newOperationContext(); - auto sorted = harnessHelper->newSortedDataInterface(false, { - {key1, loc1}, - }); +// Call savePosition() on a reverse cursor without ever calling restorePosition(). +// May be useful to run this test under valgrind to verify there are no leaks. +TEST(SortedDataInterface, SavePositionWithoutRestoreReversed) { + const std::unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper()); + const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false)); - auto cursor = sorted->newCursor(opCtx.get(), forward); - - ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); - // next() would return EOF now. - - cursor->savePositioned(); - removeFromIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); - // The restore may have seeked to EOF. - - auto insertPoint = forward ? key2 : key0; - cursor->savePositioned(); // Should still save key1 as "current position". - insertToIndex(opCtx, sorted, {{insertPoint, loc1}}); - cursor->restore(opCtx.get()); - - ASSERT_EQ(cursor->next(), IndexKeyEntry(insertPoint, loc1)); - } - - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterEOF_Forward_Unique) { - testSaveAndRestorePositionSeesNewInsertsAfterEOF(true, true); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterEOF_Forward_Standard) { - testSaveAndRestorePositionSeesNewInsertsAfterEOF(true, false); + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT(sorted->isEmpty(opCtx.get())); } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterEOF_Reverse_Unique) { - testSaveAndRestorePositionSeesNewInsertsAfterEOF(false, true); - } - TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterEOF_Reverse_Standard) { - testSaveAndRestorePositionSeesNewInsertsAfterEOF(false, false); - } - - // Make sure we restore to a RecordId at or ahead of save point if same key. - void testSaveAndRestorePositionConsidersRecordId_Forward(bool unique) { - auto harnessHelper = newHarnessHelper(); - auto opCtx = harnessHelper->newOperationContext(); - auto sorted = harnessHelper->newSortedDataInterface(unique, { - {key1, loc1}, - {key2, loc1}, - {key3, loc1}, - }); - - auto cursor = sorted->newCursor(opCtx.get()); - - ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); - - cursor->savePositioned(); - removeFromIndex(opCtx, sorted, {{key1, loc1}}); - insertToIndex(opCtx, sorted, {{key1, loc2}}); - cursor->restore(opCtx.get()); // Lands on inserted key. - - ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc2)); - - cursor->savePositioned(); - removeFromIndex(opCtx, sorted, {{key1, loc2}}); - insertToIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); // Lands after inserted. - ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); - - cursor->savePositioned(); - removeFromIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); - - cursor->savePositioned(); - insertToIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); // Lands at same point as initial save. - - // Advances from restore point since restore didn't move position. - ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1)); - } - TEST(SortedDataInterface, SaveAndRestorePositionConsidersRecordId_Forward_Standard) { - testSaveAndRestorePositionConsidersRecordId_Forward(false); - } - TEST(SortedDataInterface, SaveAndRestorePositionConsidersRecordId_Forward_Unique) { - testSaveAndRestorePositionConsidersRecordId_Forward(true); + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + { + WriteUnitOfWork uow(opCtx.get()); + ASSERT_OK(sorted->insert(opCtx.get(), key1, loc1, true)); + uow.commit(); + } } - - // Make sure we restore to a RecordId at or ahead of save point if same key on reverse cursor. - void testSaveAndRestorePositionConsidersRecordId_Reverse(bool unique) { - auto harnessHelper = newHarnessHelper(); - auto opCtx = harnessHelper->newOperationContext(); - auto sorted = harnessHelper->newSortedDataInterface(unique, { - {key0, loc1}, - {key1, loc1}, - {key2, loc2}, - }); - - auto cursor = sorted->newCursor(opCtx.get(), false); - ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2)); - - cursor->savePositioned(); - removeFromIndex(opCtx, sorted, {{key2, loc2}}); - insertToIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); - - ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); - - cursor->savePositioned(); - removeFromIndex(opCtx, sorted, {{key2, loc1}}); - insertToIndex(opCtx, sorted, {{key2, loc2}}); - cursor->restore(opCtx.get()); - - ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1)); - - cursor->savePositioned(); - removeFromIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + ASSERT_EQUALS(1, sorted->numEntries(opCtx.get())); + } + { + const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext()); + const std::unique_ptr<SortedDataInterface::Cursor> cursor( + sorted->newCursor(opCtx.get(), false)); cursor->savePositioned(); - insertToIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); // Lands at same point as initial save. - - // Advances from restore point since restore didn't move position. - ASSERT_EQ(cursor->next(), IndexKeyEntry(key0, loc1)); - } - TEST(SortedDataInterface, SaveAndRestorePositionConsidersRecordId_Reverse_Standard) { - testSaveAndRestorePositionConsidersRecordId_Reverse(false); - } - TEST(SortedDataInterface, SaveAndRestorePositionConsidersRecordId_Reverse_Unique) { - testSaveAndRestorePositionConsidersRecordId_Reverse(true); } +} + +// Ensure that restore lands as close as possible to original position, even if data inserted +// while saved. +void testSaveAndRestorePositionSeesNewInserts(bool forward, bool unique) { + auto harnessHelper = newHarnessHelper(); + auto opCtx = harnessHelper->newOperationContext(); + auto sorted = harnessHelper->newSortedDataInterface(unique, + { + {key1, loc1}, {key3, loc1}, + }); + + auto cursor = sorted->newCursor(opCtx.get(), forward); + const auto seekPoint = forward ? key1 : key3; + + ASSERT_EQ(cursor->seek(seekPoint, true), IndexKeyEntry(seekPoint, loc1)); + + cursor->savePositioned(); + insertToIndex(opCtx, sorted, {{key2, loc1}}); + cursor->restore(opCtx.get()); + + ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInserts_Forward_Unique) { + testSaveAndRestorePositionSeesNewInserts(true, true); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInserts_Forward_Standard) { + testSaveAndRestorePositionSeesNewInserts(true, false); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInserts_Reverse_Unique) { + testSaveAndRestorePositionSeesNewInserts(false, true); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInserts_Reverse_Standard) { + testSaveAndRestorePositionSeesNewInserts(false, false); +} + +// Ensure that repeated restores lands as close as possible to original position, even if data +// inserted while saved and the current position removed. +void testSaveAndRestorePositionSeesNewInsertsAfterRemove(bool forward, bool unique) { + auto harnessHelper = newHarnessHelper(); + auto opCtx = harnessHelper->newOperationContext(); + auto sorted = harnessHelper->newSortedDataInterface(unique, + { + {key1, loc1}, {key3, loc1}, + }); + + auto cursor = sorted->newCursor(opCtx.get(), forward); + const auto seekPoint = forward ? key1 : key3; + + ASSERT_EQ(cursor->seek(seekPoint, true), IndexKeyEntry(seekPoint, loc1)); + + cursor->savePositioned(); + removeFromIndex(opCtx, sorted, {{key1, loc1}}); + cursor->restore(opCtx.get()); + // The restore may have seeked since it can't return to the saved position. + + cursor->savePositioned(); // Should still save originally saved key as "current position". + insertToIndex(opCtx, sorted, {{key2, loc1}}); + cursor->restore(opCtx.get()); + + ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterRemove_Forward_Unique) { + testSaveAndRestorePositionSeesNewInsertsAfterRemove(true, true); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterRemove_Forward_Standard) { + testSaveAndRestorePositionSeesNewInsertsAfterRemove(true, false); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterRemove_Reverse_Unique) { + testSaveAndRestorePositionSeesNewInsertsAfterRemove(false, true); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterRemove_Reverse_Standard) { + testSaveAndRestorePositionSeesNewInsertsAfterRemove(false, false); +} + +// Ensure that repeated restores lands as close as possible to original position, even if data +// inserted while saved and the current position removed in a way that temporarily makes the +// cursor EOF. +void testSaveAndRestorePositionSeesNewInsertsAfterEOF(bool forward, bool unique) { + auto harnessHelper = newHarnessHelper(); + auto opCtx = harnessHelper->newOperationContext(); + auto sorted = harnessHelper->newSortedDataInterface(false, + { + {key1, loc1}, + }); + + auto cursor = sorted->newCursor(opCtx.get(), forward); + + ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); + // next() would return EOF now. + + cursor->savePositioned(); + removeFromIndex(opCtx, sorted, {{key1, loc1}}); + cursor->restore(opCtx.get()); + // The restore may have seeked to EOF. + + auto insertPoint = forward ? key2 : key0; + cursor->savePositioned(); // Should still save key1 as "current position". + insertToIndex(opCtx, sorted, {{insertPoint, loc1}}); + cursor->restore(opCtx.get()); + + ASSERT_EQ(cursor->next(), IndexKeyEntry(insertPoint, loc1)); +} + +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterEOF_Forward_Unique) { + testSaveAndRestorePositionSeesNewInsertsAfterEOF(true, true); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterEOF_Forward_Standard) { + testSaveAndRestorePositionSeesNewInsertsAfterEOF(true, false); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterEOF_Reverse_Unique) { + testSaveAndRestorePositionSeesNewInsertsAfterEOF(false, true); +} +TEST(SortedDataInterface, SaveAndRestorePositionSeesNewInsertsAfterEOF_Reverse_Standard) { + testSaveAndRestorePositionSeesNewInsertsAfterEOF(false, false); +} + +// Make sure we restore to a RecordId at or ahead of save point if same key. +void testSaveAndRestorePositionConsidersRecordId_Forward(bool unique) { + auto harnessHelper = newHarnessHelper(); + auto opCtx = harnessHelper->newOperationContext(); + auto sorted = harnessHelper->newSortedDataInterface(unique, + { + {key1, loc1}, {key2, loc1}, {key3, loc1}, + }); + + auto cursor = sorted->newCursor(opCtx.get()); + + ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); + + cursor->savePositioned(); + removeFromIndex(opCtx, sorted, {{key1, loc1}}); + insertToIndex(opCtx, sorted, {{key1, loc2}}); + cursor->restore(opCtx.get()); // Lands on inserted key. + + ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc2)); + + cursor->savePositioned(); + removeFromIndex(opCtx, sorted, {{key1, loc2}}); + insertToIndex(opCtx, sorted, {{key1, loc1}}); + cursor->restore(opCtx.get()); // Lands after inserted. + + ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); + + cursor->savePositioned(); + removeFromIndex(opCtx, sorted, {{key2, loc1}}); + cursor->restore(opCtx.get()); + + cursor->savePositioned(); + insertToIndex(opCtx, sorted, {{key2, loc1}}); + cursor->restore(opCtx.get()); // Lands at same point as initial save. + + // Advances from restore point since restore didn't move position. + ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1)); +} +TEST(SortedDataInterface, SaveAndRestorePositionConsidersRecordId_Forward_Standard) { + testSaveAndRestorePositionConsidersRecordId_Forward(false); +} +TEST(SortedDataInterface, SaveAndRestorePositionConsidersRecordId_Forward_Unique) { + testSaveAndRestorePositionConsidersRecordId_Forward(true); +} + +// Make sure we restore to a RecordId at or ahead of save point if same key on reverse cursor. +void testSaveAndRestorePositionConsidersRecordId_Reverse(bool unique) { + auto harnessHelper = newHarnessHelper(); + auto opCtx = harnessHelper->newOperationContext(); + auto sorted = harnessHelper->newSortedDataInterface(unique, + { + {key0, loc1}, {key1, loc1}, {key2, loc2}, + }); + + auto cursor = sorted->newCursor(opCtx.get(), false); + + ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2)); + + cursor->savePositioned(); + removeFromIndex(opCtx, sorted, {{key2, loc2}}); + insertToIndex(opCtx, sorted, {{key2, loc1}}); + cursor->restore(opCtx.get()); + + ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); + + cursor->savePositioned(); + removeFromIndex(opCtx, sorted, {{key2, loc1}}); + insertToIndex(opCtx, sorted, {{key2, loc2}}); + cursor->restore(opCtx.get()); + + ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1)); + + cursor->savePositioned(); + removeFromIndex(opCtx, sorted, {{key1, loc1}}); + cursor->restore(opCtx.get()); + + cursor->savePositioned(); + insertToIndex(opCtx, sorted, {{key1, loc1}}); + cursor->restore(opCtx.get()); // Lands at same point as initial save. + + // Advances from restore point since restore didn't move position. + ASSERT_EQ(cursor->next(), IndexKeyEntry(key0, loc1)); +} +TEST(SortedDataInterface, SaveAndRestorePositionConsidersRecordId_Reverse_Standard) { + testSaveAndRestorePositionConsidersRecordId_Reverse(false); +} +TEST(SortedDataInterface, SaveAndRestorePositionConsidersRecordId_Reverse_Unique) { + testSaveAndRestorePositionConsidersRecordId_Reverse(true); +} + +// Ensure that SaveUnpositioned allows later use of the cursor. +TEST(SortedDataInterface, SaveUnpositionedAndRestore) { + auto harnessHelper = newHarnessHelper(); + auto opCtx = harnessHelper->newOperationContext(); + auto sorted = harnessHelper->newSortedDataInterface(false, + { + {key1, loc1}, {key2, loc1}, {key3, loc1}, + }); + + auto cursor = sorted->newCursor(opCtx.get()); + + ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc1)); + + cursor->saveUnpositioned(); + removeFromIndex(opCtx, sorted, {{key2, loc1}}); + cursor->restore(opCtx.get()); - // Ensure that SaveUnpositioned allows later use of the cursor. - TEST(SortedDataInterface, SaveUnpositionedAndRestore) { - auto harnessHelper = newHarnessHelper(); - auto opCtx = harnessHelper->newOperationContext(); - auto sorted = harnessHelper->newSortedDataInterface(false, { - {key1, loc1}, - {key2, loc1}, - {key3, loc1}, - }); - - auto cursor = sorted->newCursor(opCtx.get()); - - ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc1)); - - cursor->saveUnpositioned(); - removeFromIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); - - ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); - - cursor->saveUnpositioned(); - cursor->restore(opCtx.get()); - - ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc1)); - } + ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); -} // namespace mongo + cursor->saveUnpositioned(); + cursor->restore(opCtx.get()); + + ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc1)); +} + +} // namespace mongo |