summaryrefslogtreecommitdiff
path: root/src/mongo/db/catalog/index_catalog_entry_impl.cpp
diff options
context:
space:
mode:
authorJudah Schvimer <judah@mongodb.com>2018-02-02 10:27:21 -0500
committerJudah Schvimer <judah@mongodb.com>2018-02-02 10:46:16 -0500
commitb2a7398e663ef090a651a93bedfc6d107a64cf33 (patch)
tree478c4e1c7e5bd5d6ebeb36c2ed50f9ac4a120d41 /src/mongo/db/catalog/index_catalog_entry_impl.cpp
parent488709dde37d13d42321b5ad8989331960602b53 (diff)
downloadmongo-b2a7398e663ef090a651a93bedfc6d107a64cf33.tar.gz
SERVER-32206 timestamp catalog change to declare index multikey
Diffstat (limited to 'src/mongo/db/catalog/index_catalog_entry_impl.cpp')
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.cpp120
1 files changed, 42 insertions, 78 deletions
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
index fdaee922b71..cbea2f00a00 100644
--- a/src/mongo/db/catalog/index_catalog_entry_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
@@ -44,6 +44,7 @@
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_parser.h"
+#include "mongo/db/multi_key_path_tracker.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/service_context.h"
@@ -203,38 +204,6 @@ void IndexCatalogEntryImpl::setHead(OperationContext* opCtx, RecordId newHead) {
_head = newHead;
}
-
-/**
- * RAII class, which associates a new RecoveryUnit with an OperationContext for the purposes
- * of simulating a side-transaction. Takes ownership of the new recovery unit and frees it at
- * destruction time.
- */
-class RecoveryUnitSwap {
-public:
- RecoveryUnitSwap(OperationContext* opCtx, RecoveryUnit* newRecoveryUnit)
- : _opCtx(opCtx),
- _oldRecoveryUnit(_opCtx->releaseRecoveryUnit()),
- _oldRecoveryUnitState(
- _opCtx->setRecoveryUnit(newRecoveryUnit, OperationContext::kNotInUnitOfWork)),
- _newRecoveryUnit(newRecoveryUnit) {}
-
- ~RecoveryUnitSwap() {
- _opCtx->releaseRecoveryUnit();
- _opCtx->setRecoveryUnit(_oldRecoveryUnit, _oldRecoveryUnitState);
- }
-
-private:
- // Not owned
- OperationContext* const _opCtx;
-
- // Owned, but life-time is not controlled
- RecoveryUnit* const _oldRecoveryUnit;
- OperationContext::RecoveryUnitState const _oldRecoveryUnitState;
-
- // Owned and life-time is controlled
- const std::unique_ptr<RecoveryUnit> _newRecoveryUnit;
-};
-
void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx,
const MultikeyPaths& multikeyPaths) {
if (!_indexTracksPathLevelMultikeyInfo && isMultikey()) {
@@ -267,56 +236,51 @@ void IndexCatalogEntryImpl::setMultikey(OperationContext* opCtx,
}
}
- {
- // Only one thread should set the multi-key value per collection, because the metadata for a
- // collection is one large document.
- Lock::ResourceLock collMDLock(
- opCtx->lockState(), ResourceId(RESOURCE_METADATA, _ns), MODE_X);
-
- if (!_indexTracksPathLevelMultikeyInfo && isMultikey()) {
- // It's possible that we raced with another thread when acquiring the MD lock. If the
- // index is already set as multikey and we don't have any path-level information to
- // update, then there's nothing more for us to do.
- return;
- }
-
- // This effectively emulates a side-transaction off the main transaction, which invoked
- // setMultikey. The reason we need is to avoid artificial WriteConflicts, which happen with
- // snapshot isolation.
- {
- StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine();
-
- // This ensures that the recovery unit is not swapped for engines that do not support
- // database level locking.
- std::unique_ptr<RecoveryUnitSwap> ruSwap;
- if (storageEngine->supportsDBLocking()) {
- ruSwap =
- stdx::make_unique<RecoveryUnitSwap>(opCtx, storageEngine->newRecoveryUnit());
- }
-
- WriteUnitOfWork wuow(opCtx);
-
- // It's possible that the index type (e.g. ascending/descending index) supports tracking
- // path-level multikey information, but this particular index doesn't.
- // CollectionCatalogEntry::setIndexIsMultikey() requires that we discard the path-level
- // multikey information in order to avoid unintentionally setting path-level multikey
- // information on an index created before 3.4.
- if (_collection->setIndexIsMultikey(
- opCtx,
- _descriptor->indexName(),
- _indexTracksPathLevelMultikeyInfo ? multikeyPaths : MultikeyPaths{})) {
- if (_infoCache) {
- LOG(1) << _ns << ": clearing plan cache - index " << _descriptor->keyPattern()
- << " set to multi key.";
- _infoCache->clearQueryCache();
- }
- }
+ MultikeyPaths paths = _indexTracksPathLevelMultikeyInfo ? multikeyPaths : MultikeyPaths{};
+
+ // On a primary, we can simply assign this write the same timestamp as the index creation,
+ // insert, or update that caused this index to become multikey. This is because if two
+ // operations concurrently try to change the index to be multikey, they will conflict and the
+ // loser will simply get a higher timestamp and go into the oplog second with a later optime.
+ //
+ // On a secondary, writes must get the timestamp of their oplog entry, and the multikey change
+ // must occur before the timestamp of the earliest write that makes the index multikey.
+ // Secondaries only serialize writes by document, not by collection. If two inserts that both
+ // make an index multikey are applied out of order, changing the index to multikey at the
+ // insert timestamps would change the index to multikey at the later timestamp, which would be
+ // wrong. To prevent this, rather than setting the index to be multikey here, we add the
+ // necessary information to the OperationContext and do the write at the timestamp of the
+ // beginning of the batch.
+ //
+ // One exception to this rule is for background indexes. Background indexes are built using
+ // a different OperationContext and thus this information would be ignored. Background index
+ // builds happen concurrently though and thus the multikey write can safely occur at the
+ // current clock time. Once a background index is committed, if a future write makes
+ // it multikey, that write will be marked as "isTrackingMultikeyPathInfo" on the applier's
+ // OperationContext and we can safely defer that write to the end of the batch.
+ if (MultikeyPathTracker::get(opCtx).isTrackingMultikeyPathInfo()) {
+ MultikeyPathInfo info;
+ info.nss = _collection->ns();
+ info.indexName = _descriptor->indexName();
+ info.multikeyPaths = paths;
+ MultikeyPathTracker::get(opCtx).addMultikeyPathInfo(info);
+ return;
+ }
- wuow.commit();
+ // It's possible that the index type (e.g. ascending/descending index) supports tracking
+ // path-level multikey information, but this particular index doesn't.
+ // CollectionCatalogEntry::setIndexIsMultikey() requires that we discard the path-level
+ // multikey information in order to avoid unintentionally setting path-level multikey
+ // information on an index created before 3.4.
+ if (_collection->setIndexIsMultikey(opCtx, _descriptor->indexName(), paths)) {
+ if (_infoCache) {
+ LOG(1) << _ns << ": clearing plan cache - index " << _descriptor->keyPattern()
+ << " set to multi key.";
+ _infoCache->clearQueryCache();
}
}
- _isMultikey.store(true);
+ opCtx->recoveryUnit()->onCommit([this] { _isMultikey.store(true); });
if (_indexTracksPathLevelMultikeyInfo) {
stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);