summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2017-08-01 08:58:21 -0400
committerGregory Wlodarek <gregory.wlodarek@mongodb.com>2017-08-01 08:58:21 -0400
commit49f9a6b840d3a56fc1947290d9f5834e6ea370a7 (patch)
treed2f54f63dc16be707e5be7ef731055f275bca838 /src/mongo
parent7cad8ba246755794c31b237531fedb09c0baa534 (diff)
downloadmongo-49f9a6b840d3a56fc1947290d9f5834e6ea370a7.tar.gz
SERVER-30350 Move the RecordStoreValidateAdaptor into its own file from CollectionImpl
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/catalog/SConscript3
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp240
-rw-r--r--src/mongo/db/storage/SConscript13
-rw-r--r--src/mongo/db/storage/record_store_validate_adaptor.cpp242
-rw-r--r--src/mongo/db/storage/record_store_validate_adaptor.h114
5 files changed, 373 insertions, 239 deletions
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 7a410ab6404..ccefcaaf8a8 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -227,6 +227,9 @@ env.Library(
'$BUILD_DIR/mongo/db/s/balancer',
'$BUILD_DIR/mongo/db/views/views_mongod',
],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/storage/record_store_validate_adaptor',
+ ],
)
env.Library(
diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp
index 46ecf9ff961..7523fac70d6 100644
--- a/src/mongo/db/catalog/collection_impl.cpp
+++ b/src/mongo/db/catalog/collection_impl.cpp
@@ -28,8 +28,6 @@
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
-#include <third_party/murmurhash3/MurmurHash3.h>
-
#include "mongo/platform/basic.h"
#include "mongo/db/catalog/collection_impl.h"
@@ -66,6 +64,7 @@
#include "mongo/db/storage/mmap_v1/mmap_v1_options.h"
#include "mongo/db/storage/record_fetcher.h"
#include "mongo/db/storage/record_store.h"
+#include "mongo/db/storage/record_store_validate_adaptor.h"
#include "mongo/db/update/update_driver.h"
#include "mongo/db/auth/user_document_parser.h" // XXX-ANDY
@@ -163,8 +162,6 @@ using std::vector;
using logger::LogComponent;
-static const int IndexKeyMaxSize = 1024; // this goes away with SERVER-3372
-
CollectionImpl::CollectionImpl(Collection* _this_init,
OperationContext* opCtx,
StringData fullNS,
@@ -1004,243 +1001,8 @@ const CollatorInterface* CollectionImpl::getDefaultCollator() const {
namespace {
-static const uint32_t kKeyCountTableSize = 1U << 22;
-
-using IndexKeyCountTable = std::array<uint64_t, kKeyCountTableSize>;
using ValidateResultsMap = std::map<std::string, ValidateResults>;
-class RecordStoreValidateAdaptor : public ValidateAdaptor {
-public:
- RecordStoreValidateAdaptor(OperationContext* opCtx,
- ValidateCmdLevel level,
- IndexCatalog* ic,
- ValidateResultsMap* irm)
- : _opCtx(opCtx), _level(level), _indexCatalog(ic), _indexNsResultsMap(irm) {
- _ikc = stdx::make_unique<IndexKeyCountTable>();
- }
-
- virtual Status validate(const RecordId& recordId, const RecordData& record, size_t* dataSize) {
- BSONObj recordBson = record.toBson();
-
- const Status status = validateBSON(
- recordBson.objdata(), recordBson.objsize(), Validator<BSONObj>::enabledBSONVersion());
- if (status.isOK()) {
- *dataSize = recordBson.objsize();
- } else {
- return status;
- }
-
- if (!_indexCatalog->haveAnyIndexes()) {
- return status;
- }
-
- IndexCatalog::IndexIterator i = _indexCatalog->getIndexIterator(_opCtx, false);
-
- while (i.more()) {
- const IndexDescriptor* descriptor = i.next();
- const string indexNs = descriptor->indexNamespace();
- ValidateResults curRecordResults;
-
- const IndexAccessMethod* iam = _indexCatalog->getIndex(descriptor);
-
- if (descriptor->isPartial()) {
- const IndexCatalogEntry* ice = _indexCatalog->getEntry(descriptor);
- if (!ice->getFilterExpression()->matchesBSON(recordBson)) {
- (*_indexNsResultsMap)[indexNs] = curRecordResults;
- continue;
- }
- }
-
- BSONObjSet documentKeySet = SimpleBSONObjComparator::kInstance.makeBSONObjSet();
- // There's no need to compute the prefixes of the indexed fields that cause the
- // index to be multikey when validating the index keys.
- MultikeyPaths* multikeyPaths = nullptr;
- iam->getKeys(recordBson,
- IndexAccessMethod::GetKeysMode::kEnforceConstraints,
- &documentKeySet,
- multikeyPaths);
-
- if (!descriptor->isMultikey(_opCtx) && documentKeySet.size() > 1) {
- string msg = str::stream() << "Index " << descriptor->indexName()
- << " is not multi-key but has more than one"
- << " key in document " << recordId;
- curRecordResults.errors.push_back(msg);
- curRecordResults.valid = false;
- }
-
- uint32_t indexNsHash;
- const auto& pattern = descriptor->keyPattern();
- const Ordering ord = Ordering::make(pattern);
- MurmurHash3_x86_32(indexNs.c_str(), indexNs.size(), 0, &indexNsHash);
-
- for (const auto& key : documentKeySet) {
- if (key.objsize() >= IndexKeyMaxSize) {
- // Index keys >= 1024 bytes are not indexed.
- _longKeys[indexNs]++;
- continue;
- }
-
- // We want to use the latest version of KeyString here.
- KeyString ks(KeyString::kLatestVersion, key, ord, recordId);
- uint32_t indexEntryHash = hashIndexEntry(ks, indexNsHash);
-
- if ((*_ikc)[indexEntryHash] == 0) {
- _indexKeyCountTableNumEntries++;
- }
- (*_ikc)[indexEntryHash]++;
- }
- (*_indexNsResultsMap)[indexNs] = curRecordResults;
- }
- return status;
- }
-
- bool tooManyIndexEntries() const {
- return _indexKeyCountTableNumEntries != 0;
- }
-
- bool tooFewIndexEntries() const {
- return _hasDocWithoutIndexEntry;
- }
-
- /**
- * Traverse the index to validate the entries and cache index keys for later use.
- */
- void traverseIndex(const IndexAccessMethod* iam,
- const IndexDescriptor* descriptor,
- ValidateResults* results,
- int64_t* numTraversedKeys) {
- auto indexNs = descriptor->indexNamespace();
- int64_t numKeys = 0;
-
- uint32_t indexNsHash;
- MurmurHash3_x86_32(indexNs.c_str(), indexNs.size(), 0, &indexNsHash);
-
- const auto& key = descriptor->keyPattern();
- const Ordering ord = Ordering::make(key);
- KeyString::Version version = KeyString::kLatestVersion;
- std::unique_ptr<KeyString> prevIndexKeyString = nullptr;
- bool isFirstEntry = true;
-
- std::unique_ptr<SortedDataInterface::Cursor> cursor = iam->newCursor(_opCtx, true);
- // Seeking to BSONObj() is equivalent to seeking to the first entry of an index.
- for (auto indexEntry = cursor->seek(BSONObj(), true); indexEntry;
- indexEntry = cursor->next()) {
-
- // We want to use the latest version of KeyString here.
- std::unique_ptr<KeyString> indexKeyString =
- stdx::make_unique<KeyString>(version, indexEntry->key, ord, indexEntry->loc);
- // Ensure that the index entries are in increasing or decreasing order.
- if (!isFirstEntry && *indexKeyString < *prevIndexKeyString) {
- if (results->valid) {
- results->errors.push_back(
- "one or more indexes are not in strictly ascending or descending "
- "order");
- }
- results->valid = false;
- }
-
- // Cache the index keys to cross-validate with documents later.
- uint32_t keyHash = hashIndexEntry(*indexKeyString, indexNsHash);
- uint64_t& indexEntryCount = (*_ikc)[keyHash];
- if (indexEntryCount != 0) {
- indexEntryCount--;
- dassert(indexEntryCount >= 0);
- if (indexEntryCount == 0) {
- _indexKeyCountTableNumEntries--;
- }
- } else {
- _hasDocWithoutIndexEntry = true;
- results->valid = false;
- }
- numKeys++;
-
- isFirstEntry = false;
- prevIndexKeyString.swap(indexKeyString);
- }
-
- _keyCounts[indexNs] = numKeys;
- *numTraversedKeys = numKeys;
- }
-
- void validateIndexKeyCount(IndexDescriptor* idx, int64_t numRecs, ValidateResults& results) {
- const string indexNs = idx->indexNamespace();
- int64_t numIndexedKeys = _keyCounts[indexNs];
- int64_t numLongKeys = _longKeys[indexNs];
- auto totalKeys = numLongKeys + numIndexedKeys;
-
- bool hasTooFewKeys = false;
- bool noErrorOnTooFewKeys = !failIndexKeyTooLong.load() && (_level != kValidateFull);
-
- if (idx->isIdIndex() && totalKeys != numRecs) {
- hasTooFewKeys = totalKeys < numRecs ? true : hasTooFewKeys;
- string msg = str::stream() << "number of _id index entries (" << numIndexedKeys
- << ") does not match the number of documents in the index ("
- << numRecs - numLongKeys << ")";
- if (noErrorOnTooFewKeys && (numIndexedKeys < numRecs)) {
- results.warnings.push_back(msg);
- } else {
- results.errors.push_back(msg);
- results.valid = false;
- }
- }
-
- if (results.valid && !idx->isMultikey(_opCtx) && totalKeys > numRecs) {
- string err = str::stream()
- << "index " << idx->indexName() << " is not multi-key, but has more entries ("
- << numIndexedKeys << ") than documents in the index (" << numRecs - numLongKeys
- << ")";
- results.errors.push_back(err);
- results.valid = false;
- }
- // Ignore any indexes with a special access method. If an access method name is given, the
- // index may be a full text, geo or special index plugin with different semantics.
- if (results.valid && !idx->isSparse() && !idx->isPartial() && !idx->isIdIndex() &&
- idx->getAccessMethodName() == "" && totalKeys < numRecs) {
- hasTooFewKeys = true;
- string msg = str::stream() << "index " << idx->indexName()
- << " is not sparse or partial, but has fewer entries ("
- << numIndexedKeys << ") than documents in the index ("
- << numRecs - numLongKeys << ")";
- if (noErrorOnTooFewKeys) {
- results.warnings.push_back(msg);
- } else {
- results.errors.push_back(msg);
- results.valid = false;
- }
- }
-
- if ((_level != kValidateFull) && hasTooFewKeys) {
- string warning = str::stream()
- << "index " << idx->indexName()
- << " has fewer keys than records. This may be the result of currently or "
- "previously running the server with the failIndexKeyTooLong parameter set to "
- "false. Please re-run the validate command with {full: true}";
- results.warnings.push_back(warning);
- }
- }
-
-private:
- std::map<string, int64_t> _longKeys;
- std::map<string, int64_t> _keyCounts;
- std::unique_ptr<IndexKeyCountTable> _ikc;
-
- uint32_t _indexKeyCountTableNumEntries = 0;
- bool _hasDocWithoutIndexEntry = false;
-
- OperationContext* _opCtx; // Not owned.
- ValidateCmdLevel _level;
- IndexCatalog* _indexCatalog; // Not owned.
- ValidateResultsMap* _indexNsResultsMap; // Not owned.
-
- uint32_t hashIndexEntry(KeyString& ks, uint32_t hash) {
- MurmurHash3_x86_32(ks.getTypeBits().getBuffer(), ks.getTypeBits().getSize(), hash, &hash);
- MurmurHash3_x86_32(ks.getBuffer(), ks.getSize(), hash, &hash);
- return hash % kKeyCountTableSize;
- }
-};
-} // namespace
-
-namespace {
void _validateRecordStore(OperationContext* opCtx,
RecordStore* recordStore,
ValidateCmdLevel level,
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript
index 46c00c785e5..508e21a1ea0 100644
--- a/src/mongo/db/storage/SConscript
+++ b/src/mongo/db/storage/SConscript
@@ -164,6 +164,19 @@ env.Library(
)
env.Library(
+ target='record_store_validate_adaptor',
+ source=[
+ 'record_store_validate_adaptor.cpp',
+ ],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/catalog/index_catalog',
+ '$BUILD_DIR/mongo/db/index/index_descriptor',
+ '$BUILD_DIR/mongo/db/index/index_access_methods',
+ '$BUILD_DIR/mongo/db/service_context',
+ ],
+ )
+
+env.Library(
target='storage_engine_lock_file',
source=[
'storage_engine_lock_file_${TARGET_OS_FAMILY}.cpp',
diff --git a/src/mongo/db/storage/record_store_validate_adaptor.cpp b/src/mongo/db/storage/record_store_validate_adaptor.cpp
new file mode 100644
index 00000000000..f4f7745c04d
--- /dev/null
+++ b/src/mongo/db/storage/record_store_validate_adaptor.cpp
@@ -0,0 +1,242 @@
+/*-
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+
+#include <third_party/murmurhash3/MurmurHash3.h>
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/index/index_access_method.h"
+#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/storage/key_string.h"
+#include "mongo/db/storage/record_store.h"
+#include "mongo/db/storage/record_store_validate_adaptor.h"
+#include "mongo/rpc/object_check.h"
+
+namespace mongo {
+
+namespace {
+uint32_t hashIndexEntry(KeyString& ks, uint32_t hash) {
+ MurmurHash3_x86_32(ks.getTypeBits().getBuffer(), ks.getTypeBits().getSize(), hash, &hash);
+ MurmurHash3_x86_32(ks.getBuffer(), ks.getSize(), hash, &hash);
+ return hash % kKeyCountTableSize;
+}
+}
+
+Status RecordStoreValidateAdaptor::validate(const RecordId& recordId,
+ const RecordData& record,
+ size_t* dataSize) {
+ BSONObj recordBson = record.toBson();
+
+ const Status status = validateBSON(
+ recordBson.objdata(), recordBson.objsize(), Validator<BSONObj>::enabledBSONVersion());
+ if (status.isOK()) {
+ *dataSize = recordBson.objsize();
+ } else {
+ return status;
+ }
+
+ if (!_indexCatalog->haveAnyIndexes()) {
+ return status;
+ }
+
+ IndexCatalog::IndexIterator i = _indexCatalog->getIndexIterator(_opCtx, false);
+
+ while (i.more()) {
+ const IndexDescriptor* descriptor = i.next();
+ const std::string indexNs = descriptor->indexNamespace();
+ ValidateResults curRecordResults;
+
+ const IndexAccessMethod* iam = _indexCatalog->getIndex(descriptor);
+
+ if (descriptor->isPartial()) {
+ const IndexCatalogEntry* ice = _indexCatalog->getEntry(descriptor);
+ if (!ice->getFilterExpression()->matchesBSON(recordBson)) {
+ (*_indexNsResultsMap)[indexNs] = curRecordResults;
+ continue;
+ }
+ }
+
+ BSONObjSet documentKeySet = SimpleBSONObjComparator::kInstance.makeBSONObjSet();
+ // There's no need to compute the prefixes of the indexed fields that cause the
+ // index to be multikey when validating the index keys.
+ MultikeyPaths* multikeyPaths = nullptr;
+ iam->getKeys(recordBson,
+ IndexAccessMethod::GetKeysMode::kEnforceConstraints,
+ &documentKeySet,
+ multikeyPaths);
+
+ if (!descriptor->isMultikey(_opCtx) && documentKeySet.size() > 1) {
+ std::string msg = str::stream() << "Index " << descriptor->indexName()
+ << " is not multi-key but has more than one"
+ << " key in document " << recordId;
+ curRecordResults.errors.push_back(msg);
+ curRecordResults.valid = false;
+ }
+
+ uint32_t indexNsHash;
+ const auto& pattern = descriptor->keyPattern();
+ const Ordering ord = Ordering::make(pattern);
+ MurmurHash3_x86_32(indexNs.c_str(), indexNs.size(), 0, &indexNsHash);
+
+ for (const auto& key : documentKeySet) {
+ if (key.objsize() >= IndexKeyMaxSize) {
+ // Index keys >= 1024 bytes are not indexed.
+ _longKeys[indexNs]++;
+ continue;
+ }
+
+ // We want to use the latest version of KeyString here.
+ KeyString ks(KeyString::kLatestVersion, key, ord, recordId);
+ uint32_t indexEntryHash = hashIndexEntry(ks, indexNsHash);
+
+ if ((*_ikc)[indexEntryHash] == 0) {
+ _indexKeyCountTableNumEntries++;
+ }
+ (*_ikc)[indexEntryHash]++;
+ }
+ (*_indexNsResultsMap)[indexNs] = curRecordResults;
+ }
+ return status;
+}
+
+void RecordStoreValidateAdaptor::traverseIndex(const IndexAccessMethod* iam,
+ const IndexDescriptor* descriptor,
+ ValidateResults* results,
+ int64_t* numTraversedKeys) {
+ auto indexNs = descriptor->indexNamespace();
+ int64_t numKeys = 0;
+
+ uint32_t indexNsHash;
+ MurmurHash3_x86_32(indexNs.c_str(), indexNs.size(), 0, &indexNsHash);
+
+ const auto& key = descriptor->keyPattern();
+ const Ordering ord = Ordering::make(key);
+ KeyString::Version version = KeyString::kLatestVersion;
+ std::unique_ptr<KeyString> prevIndexKeyString = nullptr;
+ bool isFirstEntry = true;
+
+ std::unique_ptr<SortedDataInterface::Cursor> cursor = iam->newCursor(_opCtx, true);
+ // Seeking to BSONObj() is equivalent to seeking to the first entry of an index.
+ for (auto indexEntry = cursor->seek(BSONObj(), true); indexEntry; indexEntry = cursor->next()) {
+
+ // We want to use the latest version of KeyString here.
+ std::unique_ptr<KeyString> indexKeyString =
+ stdx::make_unique<KeyString>(version, indexEntry->key, ord, indexEntry->loc);
+ // Ensure that the index entries are in increasing or decreasing order.
+ if (!isFirstEntry && *indexKeyString < *prevIndexKeyString) {
+ if (results->valid) {
+ results->errors.push_back(
+ "one or more indexes are not in strictly ascending or descending "
+ "order");
+ }
+ results->valid = false;
+ }
+
+ // Cache the index keys to cross-validate with documents later.
+ uint32_t keyHash = hashIndexEntry(*indexKeyString, indexNsHash);
+ uint64_t& indexEntryCount = (*_ikc)[keyHash];
+ if (indexEntryCount != 0) {
+ indexEntryCount--;
+ dassert(indexEntryCount >= 0);
+ if (indexEntryCount == 0) {
+ _indexKeyCountTableNumEntries--;
+ }
+ } else {
+ _hasDocWithoutIndexEntry = true;
+ results->valid = false;
+ }
+ numKeys++;
+
+ isFirstEntry = false;
+ prevIndexKeyString.swap(indexKeyString);
+ }
+
+ _keyCounts[indexNs] = numKeys;
+ *numTraversedKeys = numKeys;
+}
+
+void RecordStoreValidateAdaptor::validateIndexKeyCount(IndexDescriptor* idx,
+ int64_t numRecs,
+ ValidateResults& results) {
+ const std::string indexNs = idx->indexNamespace();
+ int64_t numIndexedKeys = _keyCounts[indexNs];
+ int64_t numLongKeys = _longKeys[indexNs];
+ auto totalKeys = numLongKeys + numIndexedKeys;
+
+ bool hasTooFewKeys = false;
+ bool noErrorOnTooFewKeys = !failIndexKeyTooLong.load() && (_level != kValidateFull);
+
+ if (idx->isIdIndex() && totalKeys != numRecs) {
+ hasTooFewKeys = totalKeys < numRecs ? true : hasTooFewKeys;
+ std::string msg = str::stream() << "number of _id index entries (" << numIndexedKeys
+ << ") does not match the number of documents in the index ("
+ << numRecs - numLongKeys << ")";
+ if (noErrorOnTooFewKeys && (numIndexedKeys < numRecs)) {
+ results.warnings.push_back(msg);
+ } else {
+ results.errors.push_back(msg);
+ results.valid = false;
+ }
+ }
+
+ if (results.valid && !idx->isMultikey(_opCtx) && totalKeys > numRecs) {
+ std::string err = str::stream()
+ << "index " << idx->indexName() << " is not multi-key, but has more entries ("
+ << numIndexedKeys << ") than documents in the index (" << numRecs - numLongKeys << ")";
+ results.errors.push_back(err);
+ results.valid = false;
+ }
+ // Ignore any indexes with a special access method. If an access method name is given, the
+ // index may be a full text, geo or special index plugin with different semantics.
+ if (results.valid && !idx->isSparse() && !idx->isPartial() && !idx->isIdIndex() &&
+ idx->getAccessMethodName() == "" && totalKeys < numRecs) {
+ hasTooFewKeys = true;
+ std::string msg = str::stream()
+ << "index " << idx->indexName() << " is not sparse or partial, but has fewer entries ("
+ << numIndexedKeys << ") than documents in the index (" << numRecs - numLongKeys << ")";
+ if (noErrorOnTooFewKeys) {
+ results.warnings.push_back(msg);
+ } else {
+ results.errors.push_back(msg);
+ results.valid = false;
+ }
+ }
+
+ if ((_level != kValidateFull) && hasTooFewKeys) {
+ std::string warning = str::stream()
+ << "index " << idx->indexName()
+ << " has fewer keys than records. This may be the result of currently or "
+ "previously running the server with the failIndexKeyTooLong parameter set to "
+ "false. Please re-run the validate command with {full: true}";
+ results.warnings.push_back(warning);
+ }
+}
+} // namespace
diff --git a/src/mongo/db/storage/record_store_validate_adaptor.h b/src/mongo/db/storage/record_store_validate_adaptor.h
new file mode 100644
index 00000000000..cccc4e06254
--- /dev/null
+++ b/src/mongo/db/storage/record_store_validate_adaptor.h
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/catalog/index_catalog.h"
+#include "mongo/db/index/index_access_method.h"
+#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/storage/record_store.h"
+
+namespace mongo {
+
+namespace {
+const uint32_t kKeyCountTableSize = 1U << 22;
+
+using IndexKeyCountTable = std::array<uint64_t, kKeyCountTableSize>;
+using ValidateResultsMap = std::map<std::string, ValidateResults>;
+}
+
+/**
+ * The record store validate adaptor is used to keep track of the index consistency during
+ * a validation that's running.
+ */
+class RecordStoreValidateAdaptor : public ValidateAdaptor {
+public:
+ RecordStoreValidateAdaptor(OperationContext* opCtx,
+ ValidateCmdLevel level,
+ IndexCatalog* ic,
+ ValidateResultsMap* irm)
+
+ : _ikc(stdx::make_unique<IndexKeyCountTable>()),
+ _opCtx(opCtx),
+ _level(level),
+ _indexCatalog(ic),
+ _indexNsResultsMap(irm) {}
+
+ /**
+ * Validates the BSON object and traverses through its key set to keep track of the
+ * index consistency.
+ */
+ virtual Status validate(const RecordId& recordId, const RecordData& record, size_t* dataSize);
+
+ /**
+ * Traverses the index getting index entriess to validate them and keep track of the index keys
+ * for index consistency.
+ */
+ void traverseIndex(const IndexAccessMethod* iam,
+ const IndexDescriptor* descriptor,
+ ValidateResults* results,
+ int64_t* numTraversedKeys);
+
+ /**
+ * Validate that the number of document keys matches the number of index keys.
+ */
+ void validateIndexKeyCount(IndexDescriptor* idx, int64_t numRecs, ValidateResults& results);
+
+ /**
+ * Returns true if there are too many index entries, otherwise return false.
+ */
+ bool tooManyIndexEntries() const {
+ return _indexKeyCountTableNumEntries != 0;
+ }
+
+ /**
+ * Returns true if there are too few index entries, which happens when a document doesn't have
+ * and index entry, otherwise return false.
+ */
+ bool tooFewIndexEntries() const {
+ return _hasDocWithoutIndexEntry;
+ }
+
+
+private:
+ std::map<std::string, int64_t> _longKeys;
+ std::map<std::string, int64_t> _keyCounts;
+ std::unique_ptr<IndexKeyCountTable> _ikc;
+
+ uint32_t _indexKeyCountTableNumEntries = 0;
+ bool _hasDocWithoutIndexEntry = false;
+
+ const int IndexKeyMaxSize = 1024; // this goes away with SERVER-3372
+
+ OperationContext* _opCtx; // Not owned.
+ ValidateCmdLevel _level;
+ IndexCatalog* _indexCatalog; // Not owned.
+ ValidateResultsMap* _indexNsResultsMap; // Not owned.
+};
+} // namespace