summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorADAM David Alan Martin <adam.martin@10gen.com>2017-03-28 13:15:16 -0400
committerADAM David Alan Martin <adam.martin@10gen.com>2017-03-28 13:15:16 -0400
commit8a988f7dfee2eaba686043cee9d7bca3366e0e8e (patch)
tree9010cfa47d3e77ce7da4c1b0b6f13c83a4e580c8
parent00ee4f5156348477b9dd3f71b747104794f766c0 (diff)
downloadmongo-8a988f7dfee2eaba686043cee9d7bca3366e0e8e.tar.gz
SERVER-28025 Sever `index_access_method`'s catalog dependency.
Slice this dependency by inserting an `IndexCatalogEntry` proxy class with a pure vtable, and exposing an inline version of `Collection::docFor`.
-rw-r--r--src/mongo/db/catalog/SConscript14
-rw-r--r--src/mongo/db/catalog/collection.cpp4
-rw-r--r--src/mongo/db/catalog/collection.h5
-rw-r--r--src/mongo/db/catalog/index_catalog.cpp2
-rw-r--r--src/mongo/db/catalog/index_catalog_entry.cpp283
-rw-r--r--src/mongo/db/catalog/index_catalog_entry.h242
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.cpp326
-rw-r--r--src/mongo/db/catalog/index_catalog_entry_impl.h219
-rw-r--r--src/mongo/db/index/SConscript1
-rw-r--r--src/mongo/db/index/index_descriptor.h2
-rw-r--r--src/mongo/db/pipeline/SConscript1
11 files changed, 724 insertions, 375 deletions
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 506750b65a2..7f1d7bb965b 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -54,7 +54,16 @@ env.CppUnitTest(
LIBDEPS=[
'$BUILD_DIR/mongo/db/query/query_test_service_context',
'index_key_validate',
- ]
+ ],
+)
+
+env.Library(
+ target='index_catalog_entry',
+ source=[
+ "index_catalog_entry.cpp",
+ ],
+ LIBDEPS=[
+ ],
)
env.Library(
@@ -74,12 +83,13 @@ env.Library(
"drop_database.cpp",
"drop_indexes.cpp",
"index_catalog.cpp",
- "index_catalog_entry.cpp",
+ "index_catalog_entry_impl.cpp",
"index_create.cpp",
"rename_collection.cpp",
],
LIBDEPS=[
'collection_options',
+ 'index_catalog_entry',
'index_key_validate',
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/db/concurrency/lock_manager',
diff --git a/src/mongo/db/catalog/collection.cpp b/src/mongo/db/catalog/collection.cpp
index de7d36b205d..be824048daa 100644
--- a/src/mongo/db/catalog/collection.cpp
+++ b/src/mongo/db/catalog/collection.cpp
@@ -280,10 +280,6 @@ vector<std::unique_ptr<RecordCursor>> Collection::getManyCursors(OperationContex
return _recordStore->getManyCursors(opCtx);
}
-Snapshotted<BSONObj> Collection::docFor(OperationContext* opCtx, const RecordId& loc) const {
- return Snapshotted<BSONObj>(opCtx->recoveryUnit()->getSnapshotId(),
- _recordStore->dataFor(opCtx, loc).releaseToBson());
-}
bool Collection::findDoc(OperationContext* opCtx,
const RecordId& loc,
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index 0d04abc5c76..16394710cca 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -228,7 +228,10 @@ public:
bool requiresIdIndex() const;
- Snapshotted<BSONObj> docFor(OperationContext* opCtx, const RecordId& loc) const;
+ Snapshotted<BSONObj> docFor(OperationContext* opCtx, const RecordId& loc) const {
+ return Snapshotted<BSONObj>(opCtx->recoveryUnit()->getSnapshotId(),
+ _recordStore->dataFor(opCtx, loc).releaseToBson());
+ }
/**
* @param out - contents set to the right docs if exists, or nothing.
diff --git a/src/mongo/db/catalog/index_catalog.cpp b/src/mongo/db/catalog/index_catalog.cpp
index 9ff5f4e1027..faf7ad134e1 100644
--- a/src/mongo/db/catalog/index_catalog.cpp
+++ b/src/mongo/db/catalog/index_catalog.cpp
@@ -147,7 +147,7 @@ IndexCatalogEntry* IndexCatalog::_setupInMemoryStructures(OperationContext* opCt
auto entry = stdx::make_unique<IndexCatalogEntry>(opCtx,
_collection->ns().ns(),
_collection->getCatalogEntry(),
- descriptorCleanup.release(),
+ std::move(descriptorCleanup),
_collection->infoCache());
std::unique_ptr<IndexAccessMethod> accessMethod(
_collection->_dbce->getIndex(opCtx, _collection->getCatalogEntry(), entry.get()));
diff --git a/src/mongo/db/catalog/index_catalog_entry.cpp b/src/mongo/db/catalog/index_catalog_entry.cpp
index ae22ff4d633..94a05fde7ac 100644
--- a/src/mongo/db/catalog/index_catalog_entry.cpp
+++ b/src/mongo/db/catalog/index_catalog_entry.cpp
@@ -1,7 +1,5 @@
-// index_catalog_entry.cpp
-
/**
-* Copyright (C) 2013 10gen Inc.
+* Copyright (C) 2017 10gen 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,
@@ -34,278 +32,40 @@
#include "mongo/db/catalog/index_catalog_entry.h"
-#include <algorithm>
-
-#include "mongo/db/catalog/collection_catalog_entry.h"
-#include "mongo/db/catalog/head_manager.h"
-#include "mongo/db/concurrency/d_concurrency.h"
-#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/index/index_access_method.h"
#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/matcher/expression.h"
-#include "mongo/db/matcher/expression_parser.h"
-#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/db/query/collation/collator_factory_interface.h"
-#include "mongo/db/service_context.h"
-#include "mongo/util/log.h"
-#include "mongo/util/scopeguard.h"
namespace mongo {
+IndexCatalogEntry::Impl::~Impl() = default;
-using std::string;
-
-class HeadManagerImpl : public HeadManager {
-public:
- HeadManagerImpl(IndexCatalogEntry* ice) : _catalogEntry(ice) {}
- virtual ~HeadManagerImpl() {}
+namespace {
+stdx::function<IndexCatalogEntry::factory_function_type> factory;
+} // namespace
- const RecordId getHead(OperationContext* opCtx) const {
- return _catalogEntry->head(opCtx);
- }
+void IndexCatalogEntry::registerFactory(decltype(factory) newFactory) {
+ factory = std::move(newFactory);
+}
- void setHead(OperationContext* opCtx, const RecordId newHead) {
- _catalogEntry->setHead(opCtx, newHead);
- }
+auto IndexCatalogEntry::makeImpl(IndexCatalogEntry* const this_,
+ OperationContext* const opCtx,
+ const StringData ns,
+ CollectionCatalogEntry* const collection,
+ std::unique_ptr<IndexDescriptor> descriptor,
+ CollectionInfoCache* const infoCache) -> std::unique_ptr<Impl> {
+ return factory(this_, opCtx, ns, collection, std::move(descriptor), infoCache);
+}
-private:
- // Not owned here.
- IndexCatalogEntry* _catalogEntry;
-};
+void IndexCatalogEntry::TUHook::hook() noexcept {}
IndexCatalogEntry::IndexCatalogEntry(OperationContext* opCtx,
StringData ns,
CollectionCatalogEntry* collection,
- IndexDescriptor* descriptor,
+ std::unique_ptr<IndexDescriptor> descriptor,
CollectionInfoCache* infoCache)
- : _ns(ns.toString()),
- _collection(collection),
- _descriptor(descriptor),
- _infoCache(infoCache),
- _headManager(new HeadManagerImpl(this)),
- _ordering(Ordering::make(descriptor->keyPattern())),
- _isReady(false) {
- _descriptor->_cachedEntry = this;
-
- _isReady = _catalogIsReady(opCtx);
- _head = _catalogHead(opCtx);
-
- {
- stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
- _isMultikey.store(_catalogIsMultikey(opCtx, &_indexMultikeyPaths));
- _indexTracksPathLevelMultikeyInfo = !_indexMultikeyPaths.empty();
- }
-
- if (BSONElement collationElement = _descriptor->getInfoElement("collation")) {
- invariant(collationElement.isABSONObj());
- BSONObj collation = collationElement.Obj();
- auto statusWithCollator =
- CollatorFactoryInterface::get(opCtx->getServiceContext())->makeFromBSON(collation);
-
- // Index spec should have already been validated.
- invariantOK(statusWithCollator.getStatus());
-
- _collator = std::move(statusWithCollator.getValue());
- }
-
- if (BSONElement filterElement = _descriptor->getInfoElement("partialFilterExpression")) {
- invariant(filterElement.isABSONObj());
- BSONObj filter = filterElement.Obj();
- StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse(
- filter, ExtensionsCallbackDisallowExtensions(), _collator.get());
- // this should be checked in create, so can blow up here
- invariantOK(statusWithMatcher.getStatus());
- _filterExpression = std::move(statusWithMatcher.getValue());
- LOG(2) << "have filter expression for " << _ns << " " << _descriptor->indexName() << " "
- << redact(filter);
- }
-}
-
-IndexCatalogEntry::~IndexCatalogEntry() {
- _descriptor->_cachedEntry = NULL; // defensive
-
- delete _headManager;
- delete _descriptor;
-}
+ : _pimpl(makeImpl(this, opCtx, ns, collection, std::move(descriptor), infoCache)) {}
void IndexCatalogEntry::init(std::unique_ptr<IndexAccessMethod> accessMethod) {
- invariant(!_accessMethod);
- _accessMethod = std::move(accessMethod);
-}
-
-const RecordId& IndexCatalogEntry::head(OperationContext* opCtx) const {
- DEV invariant(_head == _catalogHead(opCtx));
- return _head;
-}
-
-bool IndexCatalogEntry::isReady(OperationContext* opCtx) const {
- DEV invariant(_isReady == _catalogIsReady(opCtx));
- return _isReady;
-}
-
-bool IndexCatalogEntry::isMultikey() const {
- return _isMultikey.load();
-}
-
-MultikeyPaths IndexCatalogEntry::getMultikeyPaths(OperationContext* opCtx) const {
- stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
- return _indexMultikeyPaths;
-}
-
-// ---
-
-void IndexCatalogEntry::setIsReady(bool newIsReady) {
- _isReady = newIsReady;
-}
-
-class IndexCatalogEntry::SetHeadChange : public RecoveryUnit::Change {
-public:
- SetHeadChange(IndexCatalogEntry* ice, RecordId oldHead) : _ice(ice), _oldHead(oldHead) {}
-
- virtual void commit() {}
- virtual void rollback() {
- _ice->_head = _oldHead;
- }
-
- IndexCatalogEntry* _ice;
- const RecordId _oldHead;
-};
-
-void IndexCatalogEntry::setHead(OperationContext* opCtx, RecordId newHead) {
- _collection->setIndexHead(opCtx, _descriptor->indexName(), newHead);
-
- opCtx->recoveryUnit()->registerChange(new SetHeadChange(this, _head));
- _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 IndexCatalogEntry::setMultikey(OperationContext* opCtx, const MultikeyPaths& multikeyPaths) {
- if (!_indexTracksPathLevelMultikeyInfo && isMultikey()) {
- // 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;
- }
-
- if (_indexTracksPathLevelMultikeyInfo) {
- stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
- invariant(multikeyPaths.size() == _indexMultikeyPaths.size());
-
- bool newPathIsMultikey = false;
- for (size_t i = 0; i < multikeyPaths.size(); ++i) {
- if (!std::includes(_indexMultikeyPaths[i].begin(),
- _indexMultikeyPaths[i].end(),
- multikeyPaths[i].begin(),
- multikeyPaths[i].end())) {
- // If 'multikeyPaths' contains a new path component that causes this index to be
- // multikey, then we must update the index metadata in the CollectionCatalogEntry.
- newPathIsMultikey = true;
- break;
- }
- }
-
- if (!newPathIsMultikey) {
- // Otherwise, if all the path components in 'multikeyPaths' are already tracked in
- // '_indexMultikeyPaths', then there's nothing more for us to do.
- return;
- }
- }
-
- {
- // 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();
- RecoveryUnitSwap ruSwap(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();
- }
- }
-
- wuow.commit();
- }
- }
-
- _isMultikey.store(true);
-
- if (_indexTracksPathLevelMultikeyInfo) {
- stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
- for (size_t i = 0; i < multikeyPaths.size(); ++i) {
- _indexMultikeyPaths[i].insert(multikeyPaths[i].begin(), multikeyPaths[i].end());
- }
- }
-}
-
-// ----
-
-bool IndexCatalogEntry::_catalogIsReady(OperationContext* opCtx) const {
- return _collection->isIndexReady(opCtx, _descriptor->indexName());
-}
-
-RecordId IndexCatalogEntry::_catalogHead(OperationContext* opCtx) const {
- return _collection->getIndexHead(opCtx, _descriptor->indexName());
-}
-
-bool IndexCatalogEntry::_catalogIsMultikey(OperationContext* opCtx,
- MultikeyPaths* multikeyPaths) const {
- return _collection->isIndexMultikey(opCtx, _descriptor->indexName(), multikeyPaths);
+ return this->_impl().init(std::move(accessMethod));
}
// ------------------
@@ -334,7 +94,7 @@ IndexCatalogEntry* IndexCatalogEntryContainer::find(const IndexDescriptor* desc)
return nullptr;
}
-IndexCatalogEntry* IndexCatalogEntryContainer::find(const string& name) {
+IndexCatalogEntry* IndexCatalogEntryContainer::find(const std::string& name) {
for (iterator i = begin(); i != end(); ++i) {
IndexCatalogEntry* e = i->get();
if (e->descriptor()->indexName() == name)
@@ -353,5 +113,4 @@ IndexCatalogEntry* IndexCatalogEntryContainer::release(const IndexDescriptor* de
}
return nullptr;
}
-
} // namespace mongo
diff --git a/src/mongo/db/catalog/index_catalog_entry.h b/src/mongo/db/catalog/index_catalog_entry.h
index 6a36f455661..66eac041713 100644
--- a/src/mongo/db/catalog/index_catalog_entry.h
+++ b/src/mongo/db/catalog/index_catalog_entry.h
@@ -1,7 +1,5 @@
-// index_catalog_entry.h
-
/**
-* Copyright (C) 2013 10gen Inc.
+* Copyright (C) 2017 10gen 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,
@@ -39,10 +37,11 @@
#include "mongo/db/record_id.h"
#include "mongo/db/storage/snapshot_name.h"
#include "mongo/platform/atomic_word.h"
+#include "mongo/stdx/functional.h"
#include "mongo/stdx/mutex.h"
+#include "mongo/util/debug_util.h"
namespace mongo {
-
class CollatorInterface;
class CollectionCatalogEntry;
class CollectionInfoCache;
@@ -53,67 +52,157 @@ class MatchExpression;
class OperationContext;
class IndexCatalogEntry {
- MONGO_DISALLOW_COPYING(IndexCatalogEntry);
+public:
+ // This class represents the internal vtable for the (potentially polymorphic) implementation of
+ // the `IndexCatalogEntry` class. This allows us to expose an interface to this object without
+ // requiring a dependency upon the implementation's definition library.
+ class Impl {
+ public:
+ virtual ~Impl() = 0;
+
+ virtual const std::string& ns() const = 0;
+
+ virtual void init(std::unique_ptr<IndexAccessMethod> accessMethod) = 0;
+
+ virtual IndexDescriptor* descriptor() = 0;
+
+ virtual const IndexDescriptor* descriptor() const = 0;
+
+ virtual IndexAccessMethod* accessMethod() = 0;
+
+ virtual const IndexAccessMethod* accessMethod() const = 0;
+
+ virtual const Ordering& ordering() const = 0;
+
+ virtual const MatchExpression* getFilterExpression() const = 0;
+
+ virtual const CollatorInterface* getCollator() const = 0;
+
+ virtual const RecordId& head(OperationContext* opCtx) const = 0;
+
+ virtual void setHead(OperationContext* opCtx, RecordId newHead) = 0;
+
+ virtual void setIsReady(bool newIsReady) = 0;
+
+ virtual HeadManager* headManager() const = 0;
+
+ virtual bool isMultikey() const = 0;
+
+ virtual MultikeyPaths getMultikeyPaths(OperationContext* opCtx) const = 0;
+
+ virtual void setMultikey(OperationContext* opCtx, const MultikeyPaths& multikeyPaths) = 0;
+
+ virtual bool isReady(OperationContext* opCtx) const = 0;
+
+ virtual boost::optional<SnapshotName> getMinimumVisibleSnapshot() = 0;
+
+ virtual void setMinimumVisibleSnapshot(SnapshotName name) = 0;
+ };
+
+private:
+ std::unique_ptr<Impl> _pimpl;
+ struct TUHook {
+ static void hook() noexcept;
+
+ explicit inline TUHook() noexcept {
+ if (kDebugBuild)
+ this->hook();
+ }
+ };
+ inline const Impl& _impl() const {
+ TUHook{};
+ return *this->_pimpl;
+ }
+ inline Impl& _impl() {
+ TUHook{};
+ return *this->_pimpl;
+ }
+
+ static std::unique_ptr<Impl> makeImpl(IndexCatalogEntry* this_,
+ OperationContext* opCtx,
+ StringData ns,
+ CollectionCatalogEntry* collection,
+ std::unique_ptr<IndexDescriptor> descriptor,
+ CollectionInfoCache* infoCache);
public:
- IndexCatalogEntry(OperationContext* opCtx,
- StringData ns,
- CollectionCatalogEntry* collection, // not owned
- IndexDescriptor* descriptor, // ownership passes to me
- CollectionInfoCache* infoCache); // not owned, optional
+ using factory_function_type = decltype(makeImpl);
+
+ static void registerFactory(stdx::function<factory_function_type> factory);
- ~IndexCatalogEntry();
+ explicit IndexCatalogEntry(
+ OperationContext* opCtx,
+ StringData ns,
+ CollectionCatalogEntry* collection, // not owned
+ std::unique_ptr<IndexDescriptor> descriptor, // ownership passes to me
+ CollectionInfoCache* infoCache); // not owned, optional
- const std::string& ns() const {
- return _ns;
+ // Do not call this function. It exists for use with test drivers that need to inject
+ // alternative implementations.
+ explicit IndexCatalogEntry(std::unique_ptr<Impl> impl) : _pimpl(std::move(impl)) {}
+
+ inline ~IndexCatalogEntry() = default;
+
+ inline const std::string& ns() const {
+ return this->_impl().ns();
}
void init(std::unique_ptr<IndexAccessMethod> accessMethod);
- IndexDescriptor* descriptor() {
- return _descriptor;
+ inline IndexDescriptor* descriptor() {
+ return this->_impl().descriptor();
}
- const IndexDescriptor* descriptor() const {
- return _descriptor;
+
+ inline const IndexDescriptor* descriptor() const {
+ return this->_impl().descriptor();
}
- IndexAccessMethod* accessMethod() {
- return _accessMethod.get();
+ inline IndexAccessMethod* accessMethod() {
+ return this->_impl().accessMethod();
}
- const IndexAccessMethod* accessMethod() const {
- return _accessMethod.get();
+
+ inline const IndexAccessMethod* accessMethod() const {
+ return this->_impl().accessMethod();
}
- const Ordering& ordering() const {
- return _ordering;
+ inline const Ordering& ordering() const {
+ return this->_impl().ordering();
}
- const MatchExpression* getFilterExpression() const {
- return _filterExpression.get();
+ inline const MatchExpression* getFilterExpression() const {
+ return this->_impl().getFilterExpression();
}
- const CollatorInterface* getCollator() const {
- return _collator.get();
+ inline const CollatorInterface* getCollator() const {
+ return this->_impl().getCollator();
}
/// ---------------------
- const RecordId& head(OperationContext* opCtx) const;
+ inline const RecordId& head(OperationContext* const opCtx) const {
+ return this->_impl().head(opCtx);
+ }
- void setHead(OperationContext* opCtx, RecordId newHead);
+ inline void setHead(OperationContext* const opCtx, const RecordId newHead) {
+ return this->_impl().setHead(opCtx, newHead);
+ }
- void setIsReady(bool newIsReady);
+ inline void setIsReady(const bool newIsReady) {
+ return this->_impl().setIsReady(newIsReady);
+ }
- HeadManager* headManager() const {
- return _headManager;
+ inline HeadManager* headManager() const {
+ return this->_impl().headManager();
}
// --
/**
- * Returns true if this index is multikey, and returns false otherwise.
+ * Returns true if this index is multikey and false otherwise.
*/
- bool isMultikey() const;
+ inline bool isMultikey() const {
+ return this->_impl().isMultikey();
+ }
/**
* Returns the path components that cause this index to be multikey if this index supports
@@ -124,7 +213,9 @@ public:
* returns a vector with size equal to the number of elements in the index key pattern where
* each element in the vector is an empty set.
*/
- MultikeyPaths getMultikeyPaths(OperationContext* opCtx) const;
+ inline MultikeyPaths getMultikeyPaths(OperationContext* const opCtx) const {
+ return this->_impl().getMultikeyPaths(opCtx);
+ }
/**
* Sets this index to be multikey. Information regarding which newly detected path components
@@ -136,84 +227,26 @@ public:
* with size equal to the number of elements in the index key pattern. Additionally, at least
* one path component of the indexed fields must cause this index to be multikey.
*/
- void setMultikey(OperationContext* opCtx, const MultikeyPaths& multikeyPaths);
+ void setMultikey(OperationContext* const opCtx, const MultikeyPaths& multikeyPaths) {
+ return this->_impl().setMultikey(opCtx, multikeyPaths);
+ }
// if this ready is ready for queries
- bool isReady(OperationContext* opCtx) const;
+ bool isReady(OperationContext* const opCtx) const {
+ return this->_impl().isReady(opCtx);
+ }
/**
* If return value is not boost::none, reads with majority read concern using an older snapshot
* must treat this index as unfinished.
*/
boost::optional<SnapshotName> getMinimumVisibleSnapshot() {
- return _minVisibleSnapshot;
+ return this->_impl().getMinimumVisibleSnapshot();
}
- void setMinimumVisibleSnapshot(SnapshotName name) {
- _minVisibleSnapshot = name;
+ void setMinimumVisibleSnapshot(const SnapshotName name) {
+ return this->_impl().setMinimumVisibleSnapshot(name);
}
-
-private:
- class SetMultikeyChange;
- class SetHeadChange;
-
- bool _catalogIsReady(OperationContext* opCtx) const;
- RecordId _catalogHead(OperationContext* opCtx) const;
-
- /**
- * Retrieves the multikey information associated with this index from '_collection',
- *
- * See CollectionCatalogEntry::isIndexMultikey() for more details.
- */
- bool _catalogIsMultikey(OperationContext* opCtx, MultikeyPaths* multikeyPaths) const;
-
- // -----
-
- std::string _ns;
-
- CollectionCatalogEntry* _collection; // not owned here
-
- IndexDescriptor* _descriptor; // owned here
-
- CollectionInfoCache* _infoCache; // not owned here
-
- std::unique_ptr<IndexAccessMethod> _accessMethod;
-
- // Owned here.
- HeadManager* _headManager;
- std::unique_ptr<CollatorInterface> _collator;
- std::unique_ptr<MatchExpression> _filterExpression;
-
- // cached stuff
-
- Ordering _ordering; // TODO: this might be b-tree specific
- bool _isReady; // cache of NamespaceDetails info
- RecordId _head; // cache of IndexDetails
-
- // Set to true if this index supports path-level multikey tracking.
- // '_indexTracksPathLevelMultikeyInfo' is effectively const after IndexCatalogEntry::init() is
- // called.
- bool _indexTracksPathLevelMultikeyInfo = false;
-
- // Set to true if this index is multikey. '_isMultikey' serves as a cache of the information
- // stored in the NamespaceDetails or KVCatalog.
- AtomicWord<bool> _isMultikey;
-
- // Controls concurrent access to '_indexMultikeyPaths'. We acquire this mutex rather than the
- // RESOURCE_METADATA lock as a performance optimization so that it is cheaper to detect whether
- // there is actually any path-level multikey information to update or not.
- mutable stdx::mutex _indexMultikeyPathsMutex;
-
- // Non-empty only if '_indexTracksPathLevelMultikeyInfo' is true.
- //
- // If non-empty, '_indexMultikeyPaths' is a vector with size equal to the number of elements
- // in the index key pattern. Each element in the vector is an ordered set of positions (starting
- // at 0) into the corresponding indexed field that represent what prefixes of the indexed field
- // causes the index to be multikey.
- MultikeyPaths _indexMultikeyPaths;
-
- // The earliest snapshot that is allowed to read this index.
- boost::optional<SnapshotName> _minVisibleSnapshot;
};
class IndexCatalogEntryContainer {
@@ -224,6 +257,7 @@ public:
const_iterator begin() const {
return _entries.begin();
}
+
const_iterator end() const {
return _entries.end();
}
@@ -231,6 +265,7 @@ public:
iterator begin() {
return _entries.begin();
}
+
iterator end() {
return _entries.end();
}
@@ -246,6 +281,7 @@ public:
unsigned size() const {
return _entries.size();
}
+
// -----------------
/**
@@ -267,4 +303,4 @@ public:
private:
std::vector<std::unique_ptr<IndexCatalogEntry>> _entries;
};
-}
+} // namespace mongo
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.cpp b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
new file mode 100644
index 00000000000..7b0295d234b
--- /dev/null
+++ b/src/mongo/db/catalog/index_catalog_entry_impl.cpp
@@ -0,0 +1,326 @@
+/**
+* Copyright (C) 2013 10gen 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.
+*/
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kIndex
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/catalog/index_catalog_entry_impl.h"
+
+#include <algorithm>
+
+#include "mongo/base/init.h"
+#include "mongo/db/catalog/collection_catalog_entry.h"
+#include "mongo/db/catalog/head_manager.h"
+#include "mongo/db/concurrency/d_concurrency.h"
+#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/index/index_access_method.h"
+#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/matcher/expression.h"
+#include "mongo/db/matcher/expression_parser.h"
+#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/query/collation/collator_factory_interface.h"
+#include "mongo/db/service_context.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/util/log.h"
+#include "mongo/util/scopeguard.h"
+
+namespace mongo {
+namespace {
+MONGO_INITIALIZER(InitializeIndexCatalogEntryFactory)(InitializerContext* const) {
+ IndexCatalogEntry::registerFactory([](IndexCatalogEntry* const this_,
+ OperationContext* const opCtx,
+ const StringData ns,
+ CollectionCatalogEntry* const collection,
+ std::unique_ptr<IndexDescriptor> descriptor,
+ CollectionInfoCache* const infoCache) {
+ return stdx::make_unique<IndexCatalogEntryImpl>(
+ this_, opCtx, ns, collection, std::move(descriptor), infoCache);
+ });
+ return Status::OK();
+}
+} // namespace
+
+using std::string;
+
+class HeadManagerImpl : public HeadManager {
+public:
+ HeadManagerImpl(IndexCatalogEntry* ice) : _catalogEntry(ice) {}
+ virtual ~HeadManagerImpl() {}
+
+ const RecordId getHead(OperationContext* opCtx) const {
+ return _catalogEntry->head(opCtx);
+ }
+
+ void setHead(OperationContext* opCtx, const RecordId newHead) {
+ _catalogEntry->setHead(opCtx, newHead);
+ }
+
+private:
+ // Not owned here.
+ IndexCatalogEntry* _catalogEntry;
+};
+
+IndexCatalogEntryImpl::IndexCatalogEntryImpl(IndexCatalogEntry* const this_,
+ OperationContext* const opCtx,
+ const StringData ns,
+ CollectionCatalogEntry* const collection,
+ std::unique_ptr<IndexDescriptor> descriptor,
+ CollectionInfoCache* const infoCache)
+ : _ns(ns.toString()),
+ _collection(collection),
+ _descriptor(std::move(descriptor)),
+ _infoCache(infoCache),
+ _headManager(stdx::make_unique<HeadManagerImpl>(this_)),
+ _ordering(Ordering::make(_descriptor->keyPattern())),
+ _isReady(false) {
+ _descriptor->_cachedEntry = this_;
+
+ _isReady = _catalogIsReady(opCtx);
+ _head = _catalogHead(opCtx);
+
+ {
+ stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
+ _isMultikey.store(_catalogIsMultikey(opCtx, &_indexMultikeyPaths));
+ _indexTracksPathLevelMultikeyInfo = !_indexMultikeyPaths.empty();
+ }
+
+ if (BSONElement collationElement = _descriptor->getInfoElement("collation")) {
+ invariant(collationElement.isABSONObj());
+ BSONObj collation = collationElement.Obj();
+ auto statusWithCollator =
+ CollatorFactoryInterface::get(opCtx->getServiceContext())->makeFromBSON(collation);
+
+ // Index spec should have already been validated.
+ invariantOK(statusWithCollator.getStatus());
+
+ _collator = std::move(statusWithCollator.getValue());
+ }
+
+ if (BSONElement filterElement = _descriptor->getInfoElement("partialFilterExpression")) {
+ invariant(filterElement.isABSONObj());
+ BSONObj filter = filterElement.Obj();
+ StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse(
+ filter, ExtensionsCallbackDisallowExtensions(), _collator.get());
+ // this should be checked in create, so can blow up here
+ invariantOK(statusWithMatcher.getStatus());
+ _filterExpression = std::move(statusWithMatcher.getValue());
+ LOG(2) << "have filter expression for " << _ns << " " << _descriptor->indexName() << " "
+ << redact(filter);
+ }
+}
+
+IndexCatalogEntryImpl::~IndexCatalogEntryImpl() {
+ _descriptor->_cachedEntry = nullptr; // defensive
+
+ _headManager.reset();
+ _descriptor.reset();
+}
+
+void IndexCatalogEntryImpl::init(std::unique_ptr<IndexAccessMethod> accessMethod) {
+ invariant(!_accessMethod);
+ _accessMethod = std::move(accessMethod);
+}
+
+const RecordId& IndexCatalogEntryImpl::head(OperationContext* opCtx) const {
+ DEV invariant(_head == _catalogHead(opCtx));
+ return _head;
+}
+
+bool IndexCatalogEntryImpl::isReady(OperationContext* opCtx) const {
+ DEV invariant(_isReady == _catalogIsReady(opCtx));
+ return _isReady;
+}
+
+bool IndexCatalogEntryImpl::isMultikey() const {
+ return _isMultikey.load();
+}
+
+MultikeyPaths IndexCatalogEntryImpl::getMultikeyPaths(OperationContext* opCtx) const {
+ stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
+ return _indexMultikeyPaths;
+}
+
+// ---
+
+void IndexCatalogEntryImpl::setIsReady(bool newIsReady) {
+ _isReady = newIsReady;
+}
+
+class IndexCatalogEntryImpl::SetHeadChange : public RecoveryUnit::Change {
+public:
+ SetHeadChange(IndexCatalogEntryImpl* ice, RecordId oldHead) : _ice(ice), _oldHead(oldHead) {}
+
+ virtual void commit() {}
+ virtual void rollback() {
+ _ice->_head = _oldHead;
+ }
+
+ IndexCatalogEntryImpl* _ice;
+ const RecordId _oldHead;
+};
+
+void IndexCatalogEntryImpl::setHead(OperationContext* opCtx, RecordId newHead) {
+ _collection->setIndexHead(opCtx, _descriptor->indexName(), newHead);
+
+ opCtx->recoveryUnit()->registerChange(new SetHeadChange(this, _head));
+ _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()) {
+ // 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;
+ }
+
+ if (_indexTracksPathLevelMultikeyInfo) {
+ stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
+ invariant(multikeyPaths.size() == _indexMultikeyPaths.size());
+
+ bool newPathIsMultikey = false;
+ for (size_t i = 0; i < multikeyPaths.size(); ++i) {
+ if (!std::includes(_indexMultikeyPaths[i].begin(),
+ _indexMultikeyPaths[i].end(),
+ multikeyPaths[i].begin(),
+ multikeyPaths[i].end())) {
+ // If 'multikeyPaths' contains a new path component that causes this index to be
+ // multikey, then we must update the index metadata in the CollectionCatalogEntry.
+ newPathIsMultikey = true;
+ break;
+ }
+ }
+
+ if (!newPathIsMultikey) {
+ // Otherwise, if all the path components in 'multikeyPaths' are already tracked in
+ // '_indexMultikeyPaths', then there's nothing more for us to do.
+ return;
+ }
+ }
+
+ {
+ // 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();
+ RecoveryUnitSwap ruSwap(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();
+ }
+ }
+
+ wuow.commit();
+ }
+ }
+
+ _isMultikey.store(true);
+
+ if (_indexTracksPathLevelMultikeyInfo) {
+ stdx::lock_guard<stdx::mutex> lk(_indexMultikeyPathsMutex);
+ for (size_t i = 0; i < multikeyPaths.size(); ++i) {
+ _indexMultikeyPaths[i].insert(multikeyPaths[i].begin(), multikeyPaths[i].end());
+ }
+ }
+}
+
+// ----
+
+bool IndexCatalogEntryImpl::_catalogIsReady(OperationContext* opCtx) const {
+ return _collection->isIndexReady(opCtx, _descriptor->indexName());
+}
+
+RecordId IndexCatalogEntryImpl::_catalogHead(OperationContext* opCtx) const {
+ return _collection->getIndexHead(opCtx, _descriptor->indexName());
+}
+
+bool IndexCatalogEntryImpl::_catalogIsMultikey(OperationContext* opCtx,
+ MultikeyPaths* multikeyPaths) const {
+ return _collection->isIndexMultikey(opCtx, _descriptor->indexName(), multikeyPaths);
+}
+} // namespace mongo
diff --git a/src/mongo/db/catalog/index_catalog_entry_impl.h b/src/mongo/db/catalog/index_catalog_entry_impl.h
new file mode 100644
index 00000000000..593c74dc3f1
--- /dev/null
+++ b/src/mongo/db/catalog/index_catalog_entry_impl.h
@@ -0,0 +1,219 @@
+/**
+* Copyright (C) 2013 10gen 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.
+*/
+
+#pragma once
+
+#include <boost/optional.hpp>
+#include <string>
+
+#include "mongo/base/owned_pointer_vector.h"
+#include "mongo/bson/ordering.h"
+#include "mongo/db/catalog/index_catalog_entry.h"
+#include "mongo/db/index/multikey_paths.h"
+#include "mongo/db/record_id.h"
+#include "mongo/db/storage/snapshot_name.h"
+#include "mongo/platform/atomic_word.h"
+#include "mongo/stdx/mutex.h"
+
+namespace mongo {
+
+class CollatorInterface;
+class CollectionCatalogEntry;
+class CollectionInfoCache;
+class HeadManager;
+class IndexAccessMethod;
+class IndexDescriptor;
+class MatchExpression;
+class OperationContext;
+
+class IndexCatalogEntryImpl : public IndexCatalogEntry::Impl {
+ MONGO_DISALLOW_COPYING(IndexCatalogEntryImpl);
+
+public:
+ explicit IndexCatalogEntryImpl(
+ IndexCatalogEntry* this_,
+ OperationContext* opCtx,
+ StringData ns,
+ CollectionCatalogEntry* collection, // not owned
+ std::unique_ptr<IndexDescriptor> descriptor, // ownership passes to me
+ CollectionInfoCache* infoCache); // not owned, optional
+
+ ~IndexCatalogEntryImpl() final;
+
+ const std::string& ns() const final {
+ return _ns;
+ }
+
+ void init(std::unique_ptr<IndexAccessMethod> accessMethod) final;
+
+ IndexDescriptor* descriptor() final {
+ return _descriptor.get();
+ }
+ const IndexDescriptor* descriptor() const final {
+ return _descriptor.get();
+ }
+
+ IndexAccessMethod* accessMethod() final {
+ return _accessMethod.get();
+ }
+ const IndexAccessMethod* accessMethod() const final {
+ return _accessMethod.get();
+ }
+
+ const Ordering& ordering() const final {
+ return _ordering;
+ }
+
+ const MatchExpression* getFilterExpression() const final {
+ return _filterExpression.get();
+ }
+
+ const CollatorInterface* getCollator() const final {
+ return _collator.get();
+ }
+
+ /// ---------------------
+
+ const RecordId& head(OperationContext* opCtx) const final;
+
+ void setHead(OperationContext* opCtx, RecordId newHead) final;
+
+ void setIsReady(bool newIsReady) final;
+
+ HeadManager* headManager() const final {
+ return _headManager.get();
+ }
+
+ // --
+
+ /**
+ * Returns true if this index is multikey, and returns false otherwise.
+ */
+ bool isMultikey() const final;
+
+ /**
+ * Returns the path components that cause this index to be multikey if this index supports
+ * path-level multikey tracking, and returns an empty vector if path-level multikey tracking
+ * isn't supported.
+ *
+ * If this index supports path-level multikey tracking but isn't multikey, then this function
+ * returns a vector with size equal to the number of elements in the index key pattern where
+ * each element in the vector is an empty set.
+ */
+ MultikeyPaths getMultikeyPaths(OperationContext* opCtx) const final;
+
+ /**
+ * Sets this index to be multikey. Information regarding which newly detected path components
+ * cause this index to be multikey can also be specified.
+ *
+ * If this index doesn't support path-level multikey tracking, then 'multikeyPaths' is ignored.
+ *
+ * If this index supports path-level multikey tracking, then 'multikeyPaths' must be a vector
+ * with size equal to the number of elements in the index key pattern. Additionally, at least
+ * one path component of the indexed fields must cause this index to be multikey.
+ */
+ void setMultikey(OperationContext* opCtx, const MultikeyPaths& multikeyPaths) final;
+
+ // if this ready is ready for queries
+ bool isReady(OperationContext* opCtx) const final;
+
+ /**
+ * If return value is not boost::none, reads with majority read concern using an older snapshot
+ * must treat this index as unfinished.
+ */
+ boost::optional<SnapshotName> getMinimumVisibleSnapshot() final {
+ return _minVisibleSnapshot;
+ }
+
+ void setMinimumVisibleSnapshot(SnapshotName name) final {
+ _minVisibleSnapshot = name;
+ }
+
+private:
+ class SetMultikeyChange;
+ class SetHeadChange;
+
+ bool _catalogIsReady(OperationContext* opCtx) const;
+ RecordId _catalogHead(OperationContext* opCtx) const;
+
+ /**
+ * Retrieves the multikey information associated with this index from '_collection',
+ *
+ * See CollectionCatalogEntry::isIndexMultikey() for more details.
+ */
+ bool _catalogIsMultikey(OperationContext* opCtx, MultikeyPaths* multikeyPaths) const;
+
+ // -----
+
+ std::string _ns;
+
+ CollectionCatalogEntry* _collection; // not owned here
+
+ std::unique_ptr<IndexDescriptor> _descriptor; // owned here
+
+ CollectionInfoCache* _infoCache; // not owned here
+
+ std::unique_ptr<IndexAccessMethod> _accessMethod;
+
+ // Owned here.
+ std::unique_ptr<HeadManager> _headManager;
+ std::unique_ptr<CollatorInterface> _collator;
+ std::unique_ptr<MatchExpression> _filterExpression;
+
+ // cached stuff
+
+ Ordering _ordering; // TODO: this might be b-tree specific
+ bool _isReady; // cache of NamespaceDetails info
+ RecordId _head; // cache of IndexDetails
+
+ // Set to true if this index supports path-level multikey tracking.
+ // '_indexTracksPathLevelMultikeyInfo' is effectively const after IndexCatalogEntry::init() is
+ // called.
+ bool _indexTracksPathLevelMultikeyInfo = false;
+
+ // Set to true if this index is multikey. '_isMultikey' serves as a cache of the information
+ // stored in the NamespaceDetails or KVCatalog.
+ AtomicWord<bool> _isMultikey;
+
+ // Controls concurrent access to '_indexMultikeyPaths'. We acquire this mutex rather than the
+ // RESOURCE_METADATA lock as a performance optimization so that it is cheaper to detect whether
+ // there is actually any path-level multikey information to update or not.
+ mutable stdx::mutex _indexMultikeyPathsMutex;
+
+ // Non-empty only if '_indexTracksPathLevelMultikeyInfo' is true.
+ //
+ // If non-empty, '_indexMultikeyPaths' is a vector with size equal to the number of elements
+ // in the index key pattern. Each element in the vector is an ordered set of positions (starting
+ // at 0) into the corresponding indexed field that represent what prefixes of the indexed field
+ // causes the index to be multikey.
+ MultikeyPaths _indexMultikeyPaths;
+
+ // The earliest snapshot that is allowed to read this index.
+ boost::optional<SnapshotName> _minVisibleSnapshot;
+};
+} // namespace mongo
diff --git a/src/mongo/db/index/SConscript b/src/mongo/db/index/SConscript
index f651ec3f57f..545ee2720c8 100644
--- a/src/mongo/db/index/SConscript
+++ b/src/mongo/db/index/SConscript
@@ -93,6 +93,7 @@ serveronlyEnv.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/catalog/index_catalog_entry',
'$BUILD_DIR/mongo/db/concurrency/write_conflict_exception',
'$BUILD_DIR/mongo/db/storage/storage_options',
'$BUILD_DIR/mongo/db/storage/mmap_v1/btree',
diff --git a/src/mongo/db/index/index_descriptor.h b/src/mongo/db/index/index_descriptor.h
index ec7e13026d0..c0d4a4301f9 100644
--- a/src/mongo/db/index/index_descriptor.h
+++ b/src/mongo/db/index/index_descriptor.h
@@ -298,7 +298,7 @@ private:
IndexCatalogEntry* _cachedEntry;
friend class IndexCatalog;
- friend class IndexCatalogEntry;
+ friend class IndexCatalogEntryImpl;
friend class IndexCatalogEntryContainer;
};
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index 79ac52d8d45..82c6fb925d2 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -473,7 +473,6 @@ env.Library(
'$BUILD_DIR/mongo/db/index/index_access_methods',
'$BUILD_DIR/mongo/db/matcher/expressions_mongod_only',
'$BUILD_DIR/mongo/db/stats/serveronly',
- '$BUILD_DIR/mongo/db/clientcursor',
#'$BUILD_DIR/mongo/db/catalog/catalog', # CYCLE
],
LIBDEPS_TAGS=[