diff options
author | Mathias Stearn <mathias@10gen.com> | 2015-07-06 19:16:30 -0400 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2015-07-16 14:37:02 -0400 |
commit | b9d2e18ca68246e5d21ed42a846ff4094867f159 (patch) | |
tree | cdbbac6dc5ee00404cf6452f5dd70612983127e3 /src/mongo/db/storage | |
parent | c832bc753c29f91597b75fa02c0d9019c3c20b0f (diff) | |
download | mongo-b9d2e18ca68246e5d21ed42a846ff4094867f159.tar.gz |
SERVER-17364 Don't stash RecoveryUnits across getMores
We now tell PlanExecutors to detach from their OperationContexts and to shed
all storage engine resources before stashing the ClientCursor. This is a
heavier weight operation than a normal save/restoreState which is no longer
allowed to change the OperationContext.
Diffstat (limited to 'src/mongo/db/storage')
23 files changed, 211 insertions, 189 deletions
diff --git a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp index 25ebf6a5de6..25a61a9edb2 100644 --- a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp +++ b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp @@ -47,9 +47,11 @@ public: return {}; } void savePositioned() final {} - bool restore(OperationContext* txn) final { + bool restore() final { return true; } + void detachFromOperationContext() final {} + void reattachToOperationContext(OperationContext* txn) final {} }; class DevNullRecordStore : public RecordStore { diff --git a/src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp b/src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp index f40dff8e7ff..30cabb59716 100644 --- a/src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp +++ b/src/mongo/db/storage/in_memory/in_memory_btree_impl.cpp @@ -299,14 +299,11 @@ public: } void saveUnpositioned() override { - _txn = nullptr; _savedAtEnd = true; // Doing nothing with end cursor since it will do full reseek on restore. } - void restore(OperationContext* txn) override { - _txn = txn; - + void restore() override { // Always do a full seek on restore. We cannot use our last position since index // entries may have been inserted closer to our endpoint and we would need to move // over them. @@ -324,6 +321,14 @@ public: || _data.value_comp().compare(*_it, {_savedKey, _savedLoc}) != 0; } + void detachFromOperationContext() final { + _txn = nullptr; + } + + void reattachToOperationContext(OperationContext* txn) final { + _txn = txn; + } + private: bool atEndPoint() const { return _endState && _it == _endState->it; diff --git a/src/mongo/db/storage/in_memory/in_memory_record_store.cpp b/src/mongo/db/storage/in_memory/in_memory_record_store.cpp index af596f7a569..7c3b708d513 100644 --- a/src/mongo/db/storage/in_memory/in_memory_record_store.cpp +++ b/src/mongo/db/storage/in_memory/in_memory_record_store.cpp @@ -112,7 +112,7 @@ private: class InMemoryRecordStore::Cursor final : public RecordCursor { public: Cursor(OperationContext* txn, const InMemoryRecordStore& rs) - : _txn(txn), _records(rs._data->records), _isCapped(rs.isCapped()) {} + : _records(rs._data->records), _isCapped(rs.isCapped()) {} boost::optional<Record> next() final { if (_needFirstSeek) { @@ -138,18 +138,15 @@ public: } void savePositioned() final { - _txn = nullptr; if (!_needFirstSeek && !_lastMoveWasRestore) _savedId = _it == _records.end() ? RecordId() : _it->first; } void saveUnpositioned() final { - _txn = nullptr; _savedId = RecordId(); } - bool restore(OperationContext* txn) final { - _txn = txn; + bool restore() final { if (_savedId.isNull()) { _it = _records.end(); return true; @@ -162,8 +159,10 @@ public: return !(_isCapped && _lastMoveWasRestore); } + void detachFromOperationContext() final {} + void reattachToOperationContext(OperationContext* txn) final {} + private: - unowned_ptr<OperationContext> _txn; Records::const_iterator _it; bool _needFirstSeek = true; bool _lastMoveWasRestore = false; @@ -176,7 +175,7 @@ private: class InMemoryRecordStore::ReverseCursor final : public RecordCursor { public: ReverseCursor(OperationContext* txn, const InMemoryRecordStore& rs) - : _txn(txn), _records(rs._data->records), _isCapped(rs.isCapped()) {} + : _records(rs._data->records), _isCapped(rs.isCapped()) {} boost::optional<Record> next() final { if (_needFirstSeek) { @@ -212,18 +211,15 @@ public: } void savePositioned() final { - _txn = nullptr; if (!_needFirstSeek && !_lastMoveWasRestore) _savedId = _it == _records.rend() ? RecordId() : _it->first; } void saveUnpositioned() final { - _txn = nullptr; _savedId = RecordId(); } - bool restore(OperationContext* txn) final { - _txn = txn; + bool restore() final { if (_savedId.isNull()) { _it = _records.rend(); return true; @@ -239,8 +235,10 @@ public: return !(_isCapped && _lastMoveWasRestore); } + void detachFromOperationContext() final {} + void reattachToOperationContext(OperationContext* txn) final {} + private: - unowned_ptr<OperationContext> _txn; Records::const_reverse_iterator _it; bool _needFirstSeek = true; bool _lastMoveWasRestore = false; diff --git a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp index ce1aa117fef..1154b296dcf 100644 --- a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp +++ b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp @@ -197,8 +197,6 @@ public: } void savePositioned() override { - _txn = nullptr; - if (!_lastMoveWasRestore) _savedEOF = isEOF(); @@ -215,7 +213,6 @@ public: } void saveUnpositioned() override { - _txn = nullptr; // Don't leak our registration if savePositioned() was previously called. if (!_saved.bucket.isNull()) _btree->savedCursors()->unregisterCursor(&_saved); @@ -224,11 +221,7 @@ public: _savedEOF = true; } - void restore(OperationContext* txn) override { - // guard against accidental double restore - invariant(!_txn); - _txn = txn; - + void restore() override { // Always do a full seek on restore. We cannot use our last position since index // entries may have been inserted closer to our endpoint and we would need to move // over them. @@ -251,6 +244,14 @@ public: || getDiskLoc() != _saved.loc || compareKeys(getKey(), _saved.key) != 0; } + void detachFromOperationContext() final { + _txn = nullptr; + } + + void reattachToOperationContext(OperationContext* txn) final { + _txn = txn; + } + private: bool isEOF() const { return _bucket.isNull(); diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_base.h b/src/mongo/db/storage/mmap_v1/record_store_v1_base.h index 5c0437cce56..7ba485d2a8b 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_base.h +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_base.h @@ -336,9 +336,15 @@ public: boost::optional<Record> seekExact(const RecordId& id) final; void invalidate(const RecordId& dl) final; void savePositioned() final {} - bool restore(OperationContext* txn) final { + bool restore() final { return true; } + void detachFromOperationContext() final { + _txn = nullptr; + } + void reattachToOperationContext(OperationContext* txn) final { + _txn = txn; + } std::unique_ptr<RecordFetcher> fetcherForNext() const final; private: diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_capped_iterator.cpp b/src/mongo/db/storage/mmap_v1/record_store_v1_capped_iterator.cpp index 353a7f39c0c..e1487ee20fe 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_capped_iterator.cpp +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_capped_iterator.cpp @@ -99,12 +99,9 @@ void CappedRecordStoreV1Iterator::invalidate(const RecordId& id) { } } -void CappedRecordStoreV1Iterator::savePositioned() { - _txn = nullptr; -} +void CappedRecordStoreV1Iterator::savePositioned() {} -bool CappedRecordStoreV1Iterator::restore(OperationContext* txn) { - _txn = txn; +bool CappedRecordStoreV1Iterator::restore() { return !_killedByInvalidate; } diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_capped_iterator.h b/src/mongo/db/storage/mmap_v1/record_store_v1_capped_iterator.h index 0a366d9921a..3793c5cce4b 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_capped_iterator.h +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_capped_iterator.h @@ -50,7 +50,13 @@ public: boost::optional<Record> next() final; boost::optional<Record> seekExact(const RecordId& id) final; void savePositioned() final; - bool restore(OperationContext* txn) final; + bool restore() final; + void detachFromOperationContext() final { + _txn = nullptr; + } + void reattachToOperationContext(OperationContext* txn) final { + _txn = txn; + } void invalidate(const RecordId& dl) final; std::unique_ptr<RecordFetcher> fetcherForNext() const final; std::unique_ptr<RecordFetcher> fetcherForId(const RecordId& id) const final; diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_repair_iterator.h b/src/mongo/db/storage/mmap_v1/record_store_v1_repair_iterator.h index def5178ad8e..eb2dc6cb8e1 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_repair_iterator.h +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_repair_iterator.h @@ -47,12 +47,15 @@ public: boost::optional<Record> next() final; boost::optional<Record> seekExact(const RecordId& id) final; void invalidate(const RecordId& dl); - void savePositioned() final { + void savePositioned() final {} + bool restore() final { + return true; + } + void detachFromOperationContext() final { _txn = nullptr; } - bool restore(OperationContext* txn) final { + void reattachToOperationContext(OperationContext* txn) final { _txn = txn; - return true; } // Explicitly not supporting fetcherForNext(). The expected use case for this class is a diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_simple_iterator.cpp b/src/mongo/db/storage/mmap_v1/record_store_v1_simple_iterator.cpp index babfbcf26ea..5d668004b68 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_simple_iterator.cpp +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_simple_iterator.cpp @@ -108,12 +108,9 @@ void SimpleRecordStoreV1Iterator::invalidate(const RecordId& dl) { } } -void SimpleRecordStoreV1Iterator::savePositioned() { - _txn = nullptr; -} +void SimpleRecordStoreV1Iterator::savePositioned() {} -bool SimpleRecordStoreV1Iterator::restore(OperationContext* txn) { - _txn = txn; +bool SimpleRecordStoreV1Iterator::restore() { // if the collection is dropped, then the cursor should be destroyed return true; } diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_simple_iterator.h b/src/mongo/db/storage/mmap_v1/record_store_v1_simple_iterator.h index 91b0088bf72..4b74864bf1c 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_simple_iterator.h +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_simple_iterator.h @@ -50,7 +50,13 @@ public: boost::optional<Record> next() final; boost::optional<Record> seekExact(const RecordId& id) final; void savePositioned() final; - bool restore(OperationContext* txn) final; + bool restore() final; + void detachFromOperationContext() final { + _txn = nullptr; + } + void reattachToOperationContext(OperationContext* txn) final { + _txn = txn; + } void invalidate(const RecordId& dl) final; std::unique_ptr<RecordFetcher> fetcherForNext() const final; std::unique_ptr<RecordFetcher> fetcherForId(const RecordId& id) const final; diff --git a/src/mongo/db/storage/record_store.h b/src/mongo/db/storage/record_store.h index c3226fa41e1..6d4fd9dae9f 100644 --- a/src/mongo/db/storage/record_store.h +++ b/src/mongo/db/storage/record_store.h @@ -196,7 +196,24 @@ public: * * This handles restoring after either savePositioned() or saveUnpositioned(). */ - virtual bool restore(OperationContext* txn) = 0; + virtual bool restore() = 0; + + /** + * Detaches from the OperationContext and releases any storage-engine state. + * + * It is only legal to call this when in a "saved" state. While in the "detached" state, it is + * only legal to call reattachToOperationContext or the destructor. It is not legal to call + * detachFromOperationContext() while already in the detached state. + */ + virtual void detachFromOperationContext() = 0; + + /** + * Reattaches to the OperationContext and reacquires any storage-engine state. + * + * It is only legal to call this in the "detached" state. On return, the cursor is left in a + * "saved" state, so callers must still call restoreState to use this object. + */ + virtual void reattachToOperationContext(OperationContext* opCtx) = 0; /** * Inform the cursor that this id is being invalidated. diff --git a/src/mongo/db/storage/record_store_test_recorditer.cpp b/src/mongo/db/storage/record_store_test_recorditer.cpp index 3be72344c85..7d42c6bfe2a 100644 --- a/src/mongo/db/storage/record_store_test_recorditer.cpp +++ b/src/mongo/db/storage/record_store_test_recorditer.cpp @@ -316,7 +316,7 @@ TEST(RecordStoreTestHarness, RecordIteratorEOF) { ASSERT_OK(res.getStatus()); uow.commit(); - ASSERT(cursor->restore(opCtx.get())); + ASSERT(cursor->restore()); // Iterator should still be EOF. ASSERT(!cursor->next()); @@ -369,7 +369,7 @@ TEST(RecordStoreTestHarness, RecordIteratorSavePositionedRestore) { for (int i = 0; i < nToInsert; i++) { cursor->savePositioned(); cursor->savePositioned(); // It is legal to save twice in a row. - cursor->restore(opCtx.get()); + cursor->restore(); const auto record = cursor->next(); ASSERT(record); @@ -379,7 +379,7 @@ TEST(RecordStoreTestHarness, RecordIteratorSavePositionedRestore) { cursor->savePositioned(); cursor->savePositioned(); // It is legal to save twice in a row. - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT(!cursor->next()); } diff --git a/src/mongo/db/storage/record_store_test_repairiter.cpp b/src/mongo/db/storage/record_store_test_repairiter.cpp index 56abdcb6b14..4e21eccc2e1 100644 --- a/src/mongo/db/storage/record_store_test_repairiter.cpp +++ b/src/mongo/db/storage/record_store_test_repairiter.cpp @@ -158,7 +158,7 @@ TEST(RecordStoreTestHarness, GetIteratorForRepairInvalidateSingleton) { // Invalidate the record we're pointing at. cursor->savePositioned(); cursor->invalidate(idToInvalidate); - cursor->restore(opCtx.get()); + cursor->restore(); // Iterator should be EOF now because the only thing in the collection got deleted. ASSERT(!cursor->next()); diff --git a/src/mongo/db/storage/recovery_unit.h b/src/mongo/db/storage/recovery_unit.h index 3bb9d3093ea..d50da13cb95 100644 --- a/src/mongo/db/storage/recovery_unit.h +++ b/src/mongo/db/storage/recovery_unit.h @@ -53,9 +53,6 @@ public: virtual void reportState(BSONObjBuilder* b) const {} - virtual void beingReleasedFromOperationContext() {} - virtual void beingSetOnOperationContext() {} - /** * These should be called through WriteUnitOfWork rather than directly. * @@ -109,6 +106,13 @@ public: "Current storage engine does not support $readMajorityTemporaryName"}; } + /** + * Returns true if setReadFromMajorityCommittedSnapshot() has been called. + */ + virtual bool isReadingFromMajorityCommittedSnapshot() { + return false; + } + virtual SnapshotId getSnapshotId() const = 0; /** diff --git a/src/mongo/db/storage/sorted_data_interface.h b/src/mongo/db/storage/sorted_data_interface.h index 2836c7c4814..e373fa1ff44 100644 --- a/src/mongo/db/storage/sorted_data_interface.h +++ b/src/mongo/db/storage/sorted_data_interface.h @@ -327,7 +327,24 @@ public: * * This handles restoring after either savePositioned() or saveUnpositioned(). */ - virtual void restore(OperationContext* txn) = 0; + virtual void restore() = 0; + + /** + * Detaches from the OperationContext and releases any storage-engine state. + * + * It is only legal to call this when in a "saved" state. While in the "detached" state, it + * is only legal to call reattachToOperationContext or the destructor. It is not legal to + * call detachFromOperationContext() while already in the detached state. + */ + virtual void detachFromOperationContext() = 0; + + /** + * Reattaches to the OperationContext and reacquires any storage-engine state. + * + * It is only legal to call this in the "detached" state. On return, the cursor is left in a + * "saved" state, so callers must still call restoreState to use this object. + */ + virtual void reattachToOperationContext(OperationContext* opCtx) = 0; }; /** diff --git a/src/mongo/db/storage/sorted_data_interface_test_cursor_end_position.cpp b/src/mongo/db/storage/sorted_data_interface_test_cursor_end_position.cpp index 190f707c4c0..e0d31fa90d1 100644 --- a/src/mongo/db/storage/sorted_data_interface_test_cursor_end_position.cpp +++ b/src/mongo/db/storage/sorted_data_interface_test_cursor_end_position.cpp @@ -144,7 +144,7 @@ void testSetEndPosition_Seek_Forward(bool unique, bool inclusive) { cursor->saveUnpositioned(); removeFromIndex(opCtx, sorted, {{key3, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->seek(key2, true), boost::none); ASSERT_EQ(cursor->seek(key3, true), boost::none); @@ -192,7 +192,7 @@ void testSetEndPosition_Seek_Reverse(bool unique, bool inclusive) { cursor->saveUnpositioned(); removeFromIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->seek(key3, true), boost::none); ASSERT_EQ(cursor->seek(key2, true), boost::none); @@ -226,7 +226,7 @@ void testSetEndPosition_Restore_Forward(bool unique) { ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); cursor->savePositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); @@ -236,7 +236,7 @@ void testSetEndPosition_Restore_Forward(bool unique) { { {key2, loc1}, {key3, loc1}, }); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->next(), boost::none); } @@ -262,7 +262,7 @@ void testSetEndPosition_Restore_Reverse(bool unique) { ASSERT_EQ(cursor->seek(key4, true), IndexKeyEntry(key4, loc1)); cursor->savePositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1)); @@ -272,7 +272,7 @@ void testSetEndPosition_Restore_Reverse(bool unique) { { {key2, loc1}, {key3, loc1}, }); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->next(), boost::none); } @@ -309,7 +309,7 @@ void testSetEndPosition_RestoreEndCursor_Forward(bool unique) { {key2, loc1}, // in range {key3, loc1}, // out of range }); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); @@ -342,7 +342,7 @@ void testSetEndPosition_RestoreEndCursor_Reverse(bool unique) { {key2, loc1}, // in range {key3, loc1}, // out of range }); - cursor->restore(opCtx.get()); // must restore end cursor even with saveUnpositioned(). + cursor->restore(); // must restore end cursor even with saveUnpositioned(). ASSERT_EQ(cursor->seek(key4, true), IndexKeyEntry(key4, loc1)); ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1)); 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 679bb3f8c8b..c63e6ea15fe 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 @@ -75,7 +75,7 @@ TEST(SortedDataInterface, SaveAndRestorePositionWhileIterateCursor) { ASSERT_EQ(entry, IndexKeyEntry(BSON("" << i), RecordId(42, i * 2))); cursor->savePositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); } ASSERT(!cursor->next()); ASSERT_EQ(i, nToInsert); @@ -121,7 +121,7 @@ TEST(SortedDataInterface, SaveAndRestorePositionWhileIterateCursorReversed) { ASSERT_EQ(entry, IndexKeyEntry(BSON("" << i), RecordId(42, i * 2))); cursor->savePositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); } ASSERT(!cursor->next()); ASSERT_EQ(i, -1); @@ -166,7 +166,7 @@ TEST(SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeys) { ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2))); cursor->savePositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); } ASSERT(!cursor->next()); ASSERT_EQ(i, nToInsert); @@ -212,7 +212,7 @@ TEST(SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeysRev ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2))); cursor->savePositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); } ASSERT(!cursor->next()); ASSERT_EQ(i, -1); @@ -301,7 +301,7 @@ void testSaveAndRestorePositionSeesNewInserts(bool forward, bool unique) { cursor->savePositioned(); insertToIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); } @@ -335,12 +335,12 @@ void testSaveAndRestorePositionSeesNewInsertsAfterRemove(bool forward, bool uniq cursor->savePositioned(); removeFromIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); // 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()); + cursor->restore(); ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); } @@ -375,13 +375,13 @@ void testSaveAndRestorePositionSeesNewInsertsAfterEOF(bool forward, bool unique) cursor->savePositioned(); removeFromIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); // 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()); + cursor->restore(); ASSERT_EQ(cursor->next(), IndexKeyEntry(insertPoint, loc1)); } @@ -415,24 +415,24 @@ void testSaveAndRestorePositionConsidersRecordId_Forward(bool unique) { cursor->savePositioned(); removeFromIndex(opCtx, sorted, {{key1, loc1}}); insertToIndex(opCtx, sorted, {{key1, loc2}}); - cursor->restore(opCtx.get()); // Lands on inserted key. + cursor->restore(); // 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. + cursor->restore(); // Lands after inserted. ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); cursor->savePositioned(); removeFromIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); cursor->savePositioned(); insertToIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); // Lands at same point as initial save. + cursor->restore(); // Lands at same point as initial save. // Advances from restore point since restore didn't move position. ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1)); @@ -460,24 +460,24 @@ void testSaveAndRestorePositionConsidersRecordId_Reverse(bool unique) { cursor->savePositioned(); removeFromIndex(opCtx, sorted, {{key2, loc2}}); insertToIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1)); cursor->savePositioned(); removeFromIndex(opCtx, sorted, {{key2, loc1}}); insertToIndex(opCtx, sorted, {{key2, loc2}}); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1)); cursor->savePositioned(); removeFromIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); cursor->savePositioned(); insertToIndex(opCtx, sorted, {{key1, loc1}}); - cursor->restore(opCtx.get()); // Lands at same point as initial save. + cursor->restore(); // Lands at same point as initial save. // Advances from restore point since restore didn't move position. ASSERT_EQ(cursor->next(), IndexKeyEntry(key0, loc1)); @@ -504,12 +504,12 @@ TEST(SortedDataInterface, SaveUnpositionedAndRestore) { cursor->saveUnpositioned(); removeFromIndex(opCtx, sorted, {{key2, loc1}}); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1)); cursor->saveUnpositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc1)); } diff --git a/src/mongo/db/storage/sorted_data_interface_test_harness.cpp b/src/mongo/db/storage/sorted_data_interface_test_harness.cpp index 13929c7eacc..7947aae82e5 100644 --- a/src/mongo/db/storage/sorted_data_interface_test_harness.cpp +++ b/src/mongo/db/storage/sorted_data_interface_test_harness.cpp @@ -359,7 +359,7 @@ TEST(SortedDataInterface, CursorIterate1WithSaveRestore) { ASSERT_EQ(entry, IndexKeyEntry(BSON("" << n), RecordId(5, n * 2))); n++; cursor->savePositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); } ASSERT_EQUALS(N, n); } @@ -388,7 +388,7 @@ TEST(SortedDataInterface, CursorIterateAllDupKeysWithSaveRestore) { ASSERT_EQ(entry, IndexKeyEntry(BSON("" << 5), RecordId(5, n * 2))); n++; cursor->savePositioned(); - cursor->restore(opCtx.get()); + cursor->restore(); } ASSERT_EQUALS(N, n); } diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp index bd727575954..d824406dfae 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp @@ -595,8 +595,9 @@ namespace { class WiredTigerIndexCursorBase : public SortedDataInterface::Cursor { public: WiredTigerIndexCursorBase(const WiredTigerIndex& idx, OperationContext* txn, bool forward) - : _txn(txn), _cursor(idx.uri(), idx.tableId(), false, txn), _idx(idx), _forward(forward) {} - + : _txn(txn), _idx(idx), _forward(forward) { + _cursor.emplace(_idx.uri(), _idx.tableId(), false, _txn); + } boost::optional<IndexKeyEntry> next(RequestedInfo parts) override { // Advance on a cursor at the end is a no-op if (_eof) @@ -654,24 +655,16 @@ public: } void savePositioned() override { - if (!_txn) - return; // still saved - - _savedForCheck = _txn->recoveryUnit(); - - if (!wt_keeptxnopen()) { - try { - _cursor.reset(); - } catch (const WriteConflictException& wce) { - // Ignore since this is only called when we are about to kill our transaction - // anyway. - } - - // Our saved position is wherever we were when we last called updatePosition(). - // Any partially completed repositions should not effect our saved position. + try { + if (_cursor) + _cursor->reset(); + } catch (const WriteConflictException& wce) { + // Ignore since this is only called when we are about to kill our transaction + // anyway. } - _txn = NULL; + // Our saved position is wherever we were when we last called updatePosition(). + // Any partially completed repositions should not effect our saved position. } void saveUnpositioned() override { @@ -679,21 +672,30 @@ public: _eof = true; } - void restore(OperationContext* txn) override { - // Update the session handle with our new operation context. - invariant(_savedForCheck == txn->recoveryUnit()); - _txn = txn; + void restore() override { + if (!_cursor) { + _cursor.emplace(_idx.uri(), _idx.tableId(), false, _txn); + } - if (!wt_keeptxnopen()) { - if (!_eof) { - // Ensure an active session exists, so any restored cursors will bind to it - WiredTigerRecoveryUnit::get(txn)->getSession(txn); - _lastMoveWasRestore = !seekWTCursor(_key); - TRACE_CURSOR << "restore _lastMoveWasRestore:" << _lastMoveWasRestore; - } + // Ensure an active session exists, so any restored cursors will bind to it + invariant(WiredTigerRecoveryUnit::get(_txn)->getSession(_txn) == _cursor->getSession()); + + if (!_eof) { + _lastMoveWasRestore = !seekWTCursor(_key); + TRACE_CURSOR << "restore _lastMoveWasRestore:" << _lastMoveWasRestore; } } + void detachFromOperationContext() final { + _txn = nullptr; + _cursor = {}; + } + + void reattachToOperationContext(OperationContext* txn) final { + _txn = txn; + // _cursor recreated in restore() to avoid risk of WT_ROLLBACK issues. + } + protected: // Called after _key has been filled in. Must not throw WriteConflictException. virtual void updateLocAndTypeBits() = 0; @@ -738,7 +740,7 @@ protected: } void advanceWTCursor() { - WT_CURSOR* c = _cursor.get(); + WT_CURSOR* c = _cursor->get(); int ret = WT_OP_CHECK(_forward ? c->next(c) : c->prev(c)); if (ret == WT_NOTFOUND) { _cursorAtEof = true; @@ -750,7 +752,7 @@ protected: // Seeks to query. Returns true on exact match. bool seekWTCursor(const KeyString& query) { - WT_CURSOR* c = _cursor.get(); + WT_CURSOR* c = _cursor->get(); int cmp = -1; const WiredTigerItem keyItem(query.getBuffer(), query.getSize()); @@ -795,7 +797,7 @@ protected: _eof = false; - WT_CURSOR* c = _cursor.get(); + WT_CURSOR* c = _cursor->get(); WT_ITEM item; invariantWTOK(c->get_key(c, &item)); _key.resetFromBuffer(item.data, item.size); @@ -809,13 +811,10 @@ protected: } OperationContext* _txn; - WiredTigerCursor _cursor; + boost::optional<WiredTigerCursor> _cursor; const WiredTigerIndex& _idx; // not owned const bool _forward; - // Ensures we have the same RU at restore time. - RecoveryUnit* _savedForCheck; - // These are where this cursor instance is. They are not changed in the face of a failing // next(). KeyString _key; @@ -844,7 +843,7 @@ public: void updateLocAndTypeBits() override { _loc = KeyString::decodeRecordIdAtEnd(_key.getBuffer(), _key.getSize()); - WT_CURSOR* c = _cursor.get(); + WT_CURSOR* c = _cursor->get(); WT_ITEM item; invariantWTOK(c->get_value(c, &item)); BufReader br(item.data, item.size); @@ -857,8 +856,8 @@ public: WiredTigerIndexUniqueCursor(const WiredTigerIndex& idx, OperationContext* txn, bool forward) : WiredTigerIndexCursorBase(idx, txn, forward) {} - void restore(OperationContext* txn) override { - WiredTigerIndexCursorBase::restore(txn); + void restore() override { + WiredTigerIndexCursorBase::restore(); // In addition to seeking to the correct key, we also need to make sure that the loc is // on the correct side of _loc. @@ -869,7 +868,7 @@ public: // If we get here we need to look at the actual RecordId for this key and make sure we // are supposed to see it. - WT_CURSOR* c = _cursor.get(); + WT_CURSOR* c = _cursor->get(); WT_ITEM item; invariantWTOK(c->get_value(c, &item)); @@ -893,7 +892,7 @@ public: // We assume that cursors can only ever see unique indexes in their "pristine" state, // where no duplicates are possible. The cases where dups are allowed should hold // sufficient locks to ensure that no cursor ever sees them. - WT_CURSOR* c = _cursor.get(); + WT_CURSOR* c = _cursor->get(); WT_ITEM item; invariantWTOK(c->get_value(c, &item)); @@ -912,7 +911,7 @@ public: _query.resetToKey(stripFieldNames(key), _idx.ordering()); const WiredTigerItem keyItem(_query.getBuffer(), _query.getSize()); - WT_CURSOR* c = _cursor.get(); + WT_CURSOR* c = _cursor->get(); c->set_key(c, keyItem.Get()); // Using search rather than search_near. diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp index c423cd3e1f9..9008eb4ce2a 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp @@ -93,16 +93,13 @@ const std::string kWiredTigerEngineName = "wiredTiger"; class WiredTigerRecordStore::Cursor final : public RecordCursor { public: - Cursor(OperationContext* txn, - const WiredTigerRecordStore& rs, - bool forward = true, - bool forParallelCollectionScan = false) + Cursor(OperationContext* txn, const WiredTigerRecordStore& rs, bool forward = true) : _rs(rs), _txn(txn), _forward(forward), - _forParallelCollectionScan(forParallelCollectionScan), - _cursor(new WiredTigerCursor(rs.getURI(), rs.tableId(), true, txn)), - _readUntilForOplog(WiredTigerRecoveryUnit::get(txn)->getOplogReadTill()) {} + _readUntilForOplog(WiredTigerRecoveryUnit::get(txn)->getOplogReadTill()) { + _cursor.emplace(rs.getURI(), rs.tableId(), true, txn); + } boost::optional<Record> next() final { if (_eof) @@ -186,27 +183,13 @@ public: } void savePositioned() final { - // It must be safe to call save() twice in a row without calling restore(). - if (!_txn) - return; - - // the cursor and recoveryUnit are valid on restore - // so we just record the recoveryUnit to make sure - _savedRecoveryUnit = _txn->recoveryUnit(); - if (_cursor && !wt_keeptxnopen()) { - try { + try { + if (_cursor) _cursor->reset(); - } catch (const WriteConflictException& wce) { - // Ignore since this is only called when we are about to kill our transaction - // anyway. - } + } catch (const WriteConflictException& wce) { + // Ignore since this is only called when we are about to kill our transaction + // anyway. } - - if (_forParallelCollectionScan) { - // Delete the cursor since we may come back to a different RecoveryUnit - _cursor.reset(); - } - _txn = nullptr; } void saveUnpositioned() final { @@ -214,31 +197,20 @@ public: _lastReturnedId = RecordId(); } - bool restore(OperationContext* txn) final { - _txn = txn; + bool restore() final { + if (!_cursor) + _cursor.emplace(_rs.getURI(), _rs.tableId(), true, _txn); + + // This will ensure an active session exists, so any restored cursors will bind to it + invariant(WiredTigerRecoveryUnit::get(_txn)->getSession(_txn) == _cursor->getSession()); // If we've hit EOF, then this iterator is done and need not be restored. if (_eof) return true; - bool needRestore = false; - - if (_forParallelCollectionScan) { - needRestore = true; - _savedRecoveryUnit = txn->recoveryUnit(); - _cursor.reset(new WiredTigerCursor(_rs.getURI(), _rs.tableId(), true, txn)); - _forParallelCollectionScan = false; // we only do this the first time - } - invariant(_savedRecoveryUnit == txn->recoveryUnit()); - - if (!needRestore && wt_keeptxnopen()) - return true; if (_lastReturnedId.isNull()) return true; - // This will ensure an active session exists, so any restored cursors will bind to it - invariant(WiredTigerRecoveryUnit::get(txn)->getSession(txn) == _cursor->getSession()); - WT_CURSOR* c = _cursor->get(); c->set_key(c, _makeKey(_lastReturnedId)); @@ -276,6 +248,16 @@ public: return true; } + void detachFromOperationContext() final { + _txn = nullptr; + _cursor = {}; + } + + void reattachToOperationContext(OperationContext* txn) final { + _txn = txn; + // _cursor recreated in restore() to avoid risk of WT_ROLLBACK issues. + } + private: bool isVisible(const RecordId& id) { if (!_rs._isCapped) @@ -297,10 +279,8 @@ private: const WiredTigerRecordStore& _rs; OperationContext* _txn; - RecoveryUnit* _savedRecoveryUnit; // only used to sanity check between save/restore. const bool _forward; - bool _forParallelCollectionScan; // This can go away once SERVER-17364 is resolved. - std::unique_ptr<WiredTigerCursor> _cursor; + boost::optional<WiredTigerCursor> _cursor; bool _eof = false; RecordId _lastReturnedId; // If null, need to seek to first/last record. const RecordId _readUntilForOplog; @@ -906,10 +886,7 @@ std::unique_ptr<RecordCursor> WiredTigerRecordStore::getCursor(OperationContext* std::vector<std::unique_ptr<RecordCursor>> WiredTigerRecordStore::getManyCursors( OperationContext* txn) const { std::vector<std::unique_ptr<RecordCursor>> cursors(1); - cursors[0] = stdx::make_unique<Cursor>(txn, - *this, - /*forward=*/true, - /*forParallelCollectionScan=*/true); + cursors[0] = stdx::make_unique<Cursor>(txn, *this, /*forward=*/true); return cursors; } diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp index 2dd31906af8..38447246664 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp @@ -735,7 +735,7 @@ TEST(WiredTigerRecordStoreTest, CappedCursorRollover) { } // cursor should now be dead - ASSERT_FALSE(cursor->restore(cursorCtx.get())); + ASSERT_FALSE(cursor->restore()); ASSERT(!cursor->next()); } @@ -871,7 +871,7 @@ TEST(WiredTigerRecordStoreTest, CappedCursorYieldFirst) { // See that things work if you yield before you first call getNext(). cursor->savePositioned(); cursorCtx->recoveryUnit()->abandonSnapshot(); - ASSERT_TRUE(cursor->restore(cursorCtx.get())); + ASSERT_TRUE(cursor->restore()); auto record = cursor->next(); ASSERT_EQ(loc1, record->id); ASSERT(!cursor->next()); diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp index 8ee03af4311..cdc9981df78 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp @@ -378,19 +378,6 @@ void WiredTigerRecoveryUnit::_txnOpen(OperationContext* opCtx) { _active = true; } -void WiredTigerRecoveryUnit::beingReleasedFromOperationContext() { - LOG(2) << "WiredTigerRecoveryUnit::beingReleased"; - _currentlySquirreled = true; - if (_active == false && !wt_keeptxnopen()) { - _commit(); - } -} -void WiredTigerRecoveryUnit::beingSetOnOperationContext() { - LOG(2) << "WiredTigerRecoveryUnit::broughtBack"; - _currentlySquirreled = false; -} - - // --------------------- WiredTigerCursor::WiredTigerCursor(const std::string& uri, diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h b/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h index d89ef1f3964..edd2085db29 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h @@ -65,9 +65,6 @@ public: virtual void registerChange(Change*); - virtual void beingReleasedFromOperationContext(); - virtual void beingSetOnOperationContext(); - virtual void abandonSnapshot(); // un-used API @@ -80,6 +77,9 @@ public: virtual SnapshotId getSnapshotId() const; Status setReadFromMajorityCommittedSnapshot() final; + bool isReadingFromMajorityCommittedSnapshot() final { + return _readFromMajorityCommittedSnapshot; + } // ---- WT STUFF |