summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Guo <robert.guo@10gen.com>2016-04-12 17:47:30 -0400
committerRobert Guo <robert.guo@10gen.com>2016-04-29 16:07:43 -0400
commit1d58e42a8a2030d956c972dab20da9c9f01d61bf (patch)
tree05ab35edcb4dd5f30791261fa9ccee23132418a2 /src
parent98dacf304edac746ffd4f1820c0065fbf9aec7e3 (diff)
downloadmongo-1d58e42a8a2030d956c972dab20da9c9f01d61bf.tar.gz
SERVER-23055 optimize perf of hashtable used by validate()
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/catalog/collection.cpp433
-rw-r--r--src/mongo/db/catalog/collection.h5
-rw-r--r--src/mongo/db/commands/validate.cpp13
-rw-r--r--src/mongo/db/index/index_access_method.cpp3
-rw-r--r--src/mongo/db/index/index_access_method.h13
-rw-r--r--src/mongo/db/storage/devnull/devnull_kv_engine.cpp4
-rw-r--r--src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp1
-rw-r--r--src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp7
-rw-r--r--src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h3
-rw-r--r--src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp1
-rw-r--r--src/mongo/db/storage/mmap_v1/heap_record_store_btree.h3
-rw-r--r--src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp20
-rw-r--r--src/mongo/db/storage/mmap_v1/record_store_v1_base.h3
-rw-r--r--src/mongo/db/storage/record_store.h16
-rw-r--r--src/mongo/db/storage/record_store_test_validate.cpp107
-rw-r--r--src/mongo/db/storage/record_store_test_validate.h2
-rw-r--r--src/mongo/db/storage/sorted_data_interface.h6
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_fullvalidate.cpp2
-rw-r--r--src/mongo/db/storage/sorted_data_interface_test_harness.cpp2
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp1
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_index.h1
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp7
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h3
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp23
-rw-r--r--src/mongo/dbtests/validate_tests.cpp3
25 files changed, 296 insertions, 386 deletions
diff --git a/src/mongo/db/catalog/collection.cpp b/src/mongo/db/catalog/collection.cpp
index 6be536aaf0a..2bb3ae63643 100644
--- a/src/mongo/db/catalog/collection.cpp
+++ b/src/mongo/db/catalog/collection.cpp
@@ -975,318 +975,315 @@ CollatorInterface* Collection::getDefaultCollator() const {
}
namespace {
-class RecordDataValidateAdaptor : public ValidateAdaptor {
-public:
- virtual ~RecordDataValidateAdaptor() {}
- virtual Status validate(const RecordData& record, size_t* dataSize) {
- BSONObj obj = record.toBson();
- const Status status = validateBSON(obj.objdata(), obj.objsize());
- if (status.isOK())
- *dataSize = obj.objsize();
- return status;
- }
-};
+static const uint32_t kKeyCountTableSize = 1U << 22;
-using IndexKeyCountMap = std::unordered_map<uint32_t, uint64_t>;
+using IndexKeyCountTable = std::array<uint64_t, kKeyCountTableSize>;
using ValidateResultsMap = std::map<std::string, ValidateResults>;
-class IndexValidator {
+class RecordStoreValidateAdaptor : public ValidateAdaptor {
public:
- IndexValidator(bool full) : _full(full), _ikc(new IndexKeyCountMap()) {}
-
- /**
- * Create one hash map of all entries for all indexes in a collection.
- * This ensures that we only need to do one collection scan when validating
- * a document against all the indexes that point to it.
- */
- void cacheIndexInfo(OperationContext* txn,
- const IndexAccessMethod* iam,
- const IndexDescriptor* descriptor,
- long long numKeys) {
- _keyCounts[descriptor->indexNamespace()] = numKeys;
-
- if (_full) {
- std::unique_ptr<SortedDataInterface::Cursor> cursor = iam->newCursor(txn, true);
+ RecordStoreValidateAdaptor(OperationContext* txn,
+ ValidateCmdLevel level,
+ IndexCatalog* ic,
+ ValidateResultsMap* irm)
+ : _txn(txn), _level(level), _indexCatalog(ic), _indexNsResultsMap(irm) {
+ _ikc = std::unique_ptr<IndexKeyCountTable>(new IndexKeyCountTable());
+ }
- // 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()) {
- uint32_t keyHash = hashIndexEntry(indexEntry->key, indexEntry->loc);
- (*_ikc)[keyHash]++;
- }
+ virtual Status validate(const RecordId& recordId, const RecordData& record, size_t* dataSize) {
+ BSONObj recordBson = record.toBson();
+ const Status status = validateBSON(recordBson.objdata(), recordBson.objsize());
+ if (status.isOK()) {
+ *dataSize = recordBson.objsize();
+ } else {
+ return status;
}
- }
- void validateIndexes(OperationContext* txn,
- RecordStore* rs,
- IndexCatalog& indexCatalog,
- ValidateResultsMap& indexDetailNsResultsMap,
- ValidateResults* globalResults) {
- if (!indexCatalog.haveAnyIndexes()) {
- return;
+ if (_level != kValidateFull || !_indexCatalog->haveAnyIndexes()) {
+ return status;
}
- auto documentCursor = rs->getCursor(txn, true);
- // Only used to determine if any index validation failed in this function,
- // doesn't include any indexes that have already been marked as invalid.
- bool allIndexesValid = true;
- uint64_t documentCount = 0;
- int interruptInterval = 4096;
- if (_full) {
- while (const auto& document = documentCursor->next()) {
- if (!(documentCount % interruptInterval))
- txn->checkForInterrupt();
- documentCount++;
- IndexCatalog::IndexIterator i = indexCatalog.getIndexIterator(txn, false);
- RecordId documentRecordId = document->id;
-
- while (i.more()) {
- const IndexDescriptor* descriptor = i.next();
- string indexNs = descriptor->indexNamespace();
- ValidateResults& results = indexDetailNsResultsMap[indexNs];
- if (!results.valid) {
- // No point doing additional validation if the index is already invalid.
- continue;
- }
- IndexAccessMethod* iam = indexCatalog.getIndex(descriptor);
- invariant(iam);
- BSONObj documentData = document->data.toBson();
+ IndexCatalog::IndexIterator i = _indexCatalog->getIndexIterator(_txn, false);
- if (descriptor->isPartial()) {
- const IndexCatalogEntry* ice = indexCatalog.getEntry(descriptor);
- if (!ice->getFilterExpression()->matchesBSON(documentData)) {
- continue;
- }
- }
- BSONObjSet documentKeySet;
- iam->getKeys(documentData, &documentKeySet);
-
- if (documentKeySet.size() > 1) {
- if (!descriptor->isMultikey(txn)) {
- string msg = str::stream() << "Index " << descriptor->indexName()
- << " is not multi-key but has more than one"
- << " key in one or more document(s)";
- results.errors.push_back(msg);
- results.valid = false;
- }
- }
+ while (i.more()) {
+ const IndexDescriptor* descriptor = i.next();
+ const string indexNs = descriptor->indexNamespace();
+ ValidateResults& results = (*_indexNsResultsMap)[indexNs];
+ if (!results.valid) {
+ // No point doing additional validation if the index is already invalid.
+ continue;
+ }
- for (const auto& key : documentKeySet) {
- if (key.objsize() >= IndexKeyMaxSize) {
- // Index keys >= 1024 bytes are not indexed.
- _longKeys[indexNs]++;
- continue;
- }
- uint32_t indexEntryHash = hashIndexEntry(key, documentRecordId);
- if (_ikc->find(indexEntryHash) != _ikc->end()) {
- (*_ikc)[indexEntryHash]--;
-
- dassert((*_ikc)[indexEntryHash] >= 0);
- if ((*_ikc)[indexEntryHash] == 0) {
- _ikc->erase(indexEntryHash);
- }
- } else {
- allIndexesValid = false;
- results.valid = false;
- }
- }
+ const IndexAccessMethod* iam = _indexCatalog->getIndex(descriptor);
+ BSONObjSet documentKeySet;
+ iam->getKeys(recordBson, &documentKeySet);
+
+ if (!descriptor->isMultikey(_txn) && documentKeySet.size() > 1) {
+ string msg = str::stream() << "Index " << descriptor->indexName()
+ << " is not multi-key but has more than one"
+ << " key in one or more document(s)";
+ results.errors.push_back(msg);
+ results.valid = false;
+ }
+
+ if (descriptor->isPartial()) {
+ const IndexCatalogEntry* ice = _indexCatalog->getEntry(descriptor);
+ if (!ice->getFilterExpression()->matchesBSON(recordBson)) {
+ continue;
}
}
- if (!_ikc->empty()) {
- allIndexesValid = false;
- for (auto& it : indexDetailNsResultsMap) {
- // Marking all indexes as invalid since we don't know which one failed.
- ValidateResults& r = it.second;
- r.valid = false;
+ for (const auto& key : documentKeySet) {
+ if (key.objsize() >= IndexKeyMaxSize) {
+ // Index keys >= 1024 bytes are not indexed.
+ _longKeys[indexNs]++;
+ continue;
+ }
+ uint32_t indexEntryHash = hashIndexEntry(key, recordId);
+ uint64_t& indexEntryCount = (*_ikc)[indexEntryHash];
+
+ if (indexEntryCount != 0) {
+ indexEntryCount--;
+ dassert(indexEntryCount >= 0);
+ if (indexEntryCount == 0) {
+ _indexKeyCountTableNumEntries--;
+ }
+ } else {
+ _hasDocWithoutIndexEntry = true;
+ results.valid = false;
}
}
}
+ return status;
+ }
- // Validate Index Key Count.
- if (allIndexesValid) {
- IndexCatalog::IndexIterator i = indexCatalog.getIndexIterator(txn, false);
- while (i.more()) {
- IndexDescriptor* descriptor = i.next();
- ValidateResults& results = indexDetailNsResultsMap[descriptor->indexNamespace()];
+ bool tooManyIndexEntries() const {
+ return _indexKeyCountTableNumEntries != 0;
+ }
+
+ bool tooFewIndexEntries() const {
+ return _hasDocWithoutIndexEntry;
+ }
+
+ /**
+ * Create one hash map of all entries for all indexes in a collection.
+ * This ensures that we only need to do one collection scan when validating
+ * a document against all the indexes that point to it.
+ */
+ void cacheIndexInfo(const IndexAccessMethod* iam,
+ const IndexDescriptor* descriptor,
+ long long numKeys) {
+ _keyCounts[descriptor->indexNamespace()] = numKeys;
- if (results.valid) {
- validateIndexKeyCount(txn, descriptor, rs->numRecords(txn), &results);
+ if (_level == kValidateFull) {
+ std::unique_ptr<SortedDataInterface::Cursor> cursor = iam->newCursor(_txn, 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()) {
+ uint32_t keyHash = hashIndexEntry(indexEntry->key, indexEntry->loc);
+ if ((*_ikc)[keyHash] == 0) {
+ _indexKeyCountTableNumEntries++;
}
- allIndexesValid = results.valid ? allIndexesValid : false;
+ (*_ikc)[keyHash]++;
}
}
-
- if (!allIndexesValid) {
- string msg = "One or more indexes contain invalid index entries.";
- globalResults->errors.push_back(msg);
- }
}
-private:
- bool _full;
- std::unique_ptr<IndexKeyCountMap> _ikc;
- std::map<string, long long> _longKeys;
- std::map<string, long long> _keyCounts;
-
- void validateIndexKeyCount(OperationContext* txn,
- IndexDescriptor* idx,
- int64_t numRecs,
- ValidateResults* results) {
- string indexNs = idx->indexNamespace();
+ void validateIndexKeyCount(IndexDescriptor* idx, int64_t numRecs, ValidateResults& results) {
+ const string indexNs = idx->indexNamespace();
long long numIndexedKeys = _keyCounts[indexNs];
long long numLongKeys = _longKeys[indexNs];
auto totalKeys = numLongKeys + numIndexedKeys;
bool hasTooFewKeys = false;
+ bool noErrorOnTooFewKeys = !failIndexKeyTooLong && (_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 (!failIndexKeyTooLong && !_full && (numIndexedKeys < numRecs)) {
- results->warnings.push_back(msg);
+ if (noErrorOnTooFewKeys && (numIndexedKeys < numRecs)) {
+ results.warnings.push_back(msg);
} else {
- results->errors.push_back(msg);
- results->valid = false;
+ results.errors.push_back(msg);
+ results.valid = false;
}
}
- if (results->valid && !idx->isMultikey(txn) && totalKeys > numRecs) {
+ if (results.valid && !idx->isMultikey(_txn) && 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;
+ results.errors.push_back(err);
+ results.valid = false;
}
- // Ensure normal indexes have at least as many keys as the number of documents in the
- // collection.
- if (results->valid && !idx->isSparse() && !idx->isPartial() && !idx->isIdIndex() &&
+ // 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 (!failIndexKeyTooLong && !_full) {
- results->warnings.push_back(msg);
+ if (noErrorOnTooFewKeys) {
+ results.warnings.push_back(msg);
} else {
- results->errors.push_back(msg);
- results->valid = false;
+ results.errors.push_back(msg);
+ results.valid = false;
}
}
- if (!_full && hasTooFewKeys) {
+ 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);
+ results.warnings.push_back(warning);
}
}
- uint32_t hashIndexEntry(const BSONObj& key, RecordId& loc) {
+private:
+ std::map<string, long long> _longKeys;
+ std::map<string, long long> _keyCounts;
+ std::unique_ptr<IndexKeyCountTable> _ikc;
+
+ uint32_t _indexKeyCountTableNumEntries = 0;
+ bool _hasDocWithoutIndexEntry = false;
+
+ OperationContext* _txn; // Not owned.
+ ValidateCmdLevel _level;
+ IndexCatalog* _indexCatalog; // Not owned.
+ ValidateResultsMap* _indexNsResultsMap; // Not owned.
+
+ uint32_t hashIndexEntry(const BSONObj& key, const RecordId& loc) {
uint32_t hash;
// We're only using KeyString to get something hashable here, so version doesn't matter.
KeyString ks(KeyString::Version::V1, key, Ordering::make(BSONObj()), loc);
MurmurHash3_x86_32(ks.getTypeBits().getBuffer(), ks.getTypeBits().getSize(), 0, &hash);
MurmurHash3_x86_32(ks.getBuffer(), ks.getSize(), hash, &hash);
- // Take the first 25 bits to conserve memory.
- return hash >> 7;
+ return hash % kKeyCountTableSize;
}
};
} // namespace
Status Collection::validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateResults* results,
BSONObjBuilder* output) {
dassert(txn->lockState()->isCollectionLockedForMode(ns().toString(), MODE_IS));
- RecordDataValidateAdaptor adaptor;
- Status status = _recordStore->validate(txn, full, scanData, &adaptor, results, output);
- if (!status.isOK())
- return status;
+ try {
+ ValidateResultsMap indexNsResultsMap;
+ std::unique_ptr<RecordStoreValidateAdaptor> indexValidator(
+ new RecordStoreValidateAdaptor(txn, level, &_indexCatalog, &indexNsResultsMap));
+
+ BSONObjBuilder keysPerIndex; // not using subObjStart to be exception safe
+ IndexCatalog::IndexIterator i = _indexCatalog.getIndexIterator(txn, false);
+
+ // Validate Indexes.
+ while (i.more()) {
+ txn->checkForInterrupt();
+ const IndexDescriptor* descriptor = i.next();
+ log(LogComponent::kIndex) << "validating index " << descriptor->indexNamespace()
+ << endl;
+ IndexAccessMethod* iam = _indexCatalog.getIndex(descriptor);
+ ValidateResults curIndexResults;
+ int64_t numKeys;
+ iam->validate(txn, &numKeys, &curIndexResults);
+ keysPerIndex.appendNumber(descriptor->indexNamespace(),
+ static_cast<long long>(numKeys));
+
+ indexNsResultsMap[descriptor->indexNamespace()] = curIndexResults;
+ if (curIndexResults.valid) {
+ indexValidator->cacheIndexInfo(iam, descriptor, numKeys);
+ } else {
+ results->valid = false;
+ }
+ }
+
+ // Validate RecordStore and, if `level == kValidateFull`, cross validate indexes and
+ // RecordStore.
+ if (results->valid) {
+ auto status = _recordStore->validate(txn, level, indexValidator.get(), results, output);
+ // RecordStore::validate always returns Status::OK(). Errors are reported through
+ // `results`.
+ dassert(status.isOK());
- { // indexes
- int idxn = 0;
- try {
- // Only applicable when 'full' validation is requested.
- std::unique_ptr<BSONObjBuilder> indexDetails(full ? new BSONObjBuilder() : NULL);
- std::unique_ptr<IndexValidator> indexValidator(new IndexValidator(full));
- ValidateResultsMap indexDetailNsResultsMap;
- BSONObjBuilder keysPerIndex; // not using subObjStart to be exception safe
+ if (indexValidator->tooManyIndexEntries()) {
+ for (auto& it : indexNsResultsMap) {
+ // Marking all indexes as invalid since we don't know which one failed.
+ ValidateResults& r = it.second;
+ r.valid = false;
+ }
+ string msg = "One or more indexes contain invalid index entries.";
+ results->errors.push_back(msg);
+ results->valid = false;
+ } else if (indexValidator->tooFewIndexEntries()) {
+ results->valid = false;
+ }
+ }
+ // Validate index key count.
+ if (results->valid) {
IndexCatalog::IndexIterator i = _indexCatalog.getIndexIterator(txn, false);
while (i.more()) {
- txn->checkForInterrupt();
- const IndexDescriptor* descriptor = i.next();
- log(LogComponent::kIndex) << "validating index " << descriptor->indexNamespace()
- << endl;
- IndexAccessMethod* iam = _indexCatalog.getIndex(descriptor);
- invariant(iam);
-
- ValidateResults curIndexResults;
- int64_t numKeys;
- iam->validate(txn, full, &numKeys, &curIndexResults);
- keysPerIndex.appendNumber(descriptor->indexNamespace(),
- static_cast<long long>(numKeys));
-
- indexDetailNsResultsMap[descriptor->indexNamespace()] = curIndexResults;
+ IndexDescriptor* descriptor = i.next();
+ ValidateResults& curIndexResults = indexNsResultsMap[descriptor->indexNamespace()];
+
if (curIndexResults.valid) {
- indexValidator->cacheIndexInfo(txn, iam, descriptor, numKeys);
+ indexValidator->validateIndexKeyCount(
+ descriptor, _recordStore->numRecords(txn), curIndexResults);
}
-
- idxn++;
}
+ }
- indexValidator->validateIndexes(
- txn, _recordStore, _indexCatalog, indexDetailNsResultsMap, results);
+ std::unique_ptr<BSONObjBuilder> indexDetails(level == kValidateFull ? new BSONObjBuilder()
+ : NULL);
- for (auto& it : indexDetailNsResultsMap) {
- std::string indexNs = it.first;
- ValidateResults& vr = it.second;
+ // Report index validation results.
+ for (const auto& it : indexNsResultsMap) {
+ const std::string indexNs = it.first;
+ const ValidateResults& vr = it.second;
- if (indexDetails.get()) {
- BSONObjBuilder bob(indexDetails->subobjStart(indexNs));
- bob.appendBool("valid", vr.valid);
+ if (!vr.valid) {
+ results->valid = false;
+ }
- if (!vr.warnings.empty()) {
- bob.append("warnings", vr.warnings);
- }
+ if (indexDetails.get()) {
+ BSONObjBuilder bob(indexDetails->subobjStart(indexNs));
+ bob.appendBool("valid", vr.valid);
- if (!vr.errors.empty()) {
- bob.append("errors", vr.errors);
- }
+ if (!vr.warnings.empty()) {
+ bob.append("warnings", vr.warnings);
}
- results->warnings.insert(
- results->warnings.end(), vr.warnings.begin(), vr.warnings.end());
- results->errors.insert(results->errors.end(), vr.errors.begin(), vr.errors.end());
-
- if (!vr.valid) {
- results->valid = false;
+ if (!vr.errors.empty()) {
+ bob.append("errors", vr.errors);
}
}
- output->append("nIndexes", _indexCatalog.numIndexesReady(txn));
- output->append("keysPerIndex", keysPerIndex.done());
- if (indexDetails.get()) {
- output->append("indexDetails", indexDetails->done());
- }
- } catch (DBException& e) {
- if (ErrorCodes::isInterruption(ErrorCodes::Error(e.getCode()))) {
- return e.toStatus();
- }
- string err = str::stream() << "exception during index validate idxn "
- << BSONObjBuilder::numStr(idxn) << ": " << e.toString();
- results->errors.push_back(err);
- results->valid = false;
+ results->warnings.insert(
+ results->warnings.end(), vr.warnings.begin(), vr.warnings.end());
+ results->errors.insert(results->errors.end(), vr.errors.begin(), vr.errors.end());
+ }
+
+ output->append("nIndexes", _indexCatalog.numIndexesReady(txn));
+ output->append("keysPerIndex", keysPerIndex.done());
+ if (indexDetails.get()) {
+ output->append("indexDetails", indexDetails->done());
+ }
+ } catch (DBException& e) {
+ if (ErrorCodes::isInterruption(ErrorCodes::Error(e.getCode()))) {
+ return e.toStatus();
}
+ string err = str::stream() << "exception during index validation: " << e.toString();
+ results->errors.push_back(err);
+ results->valid = false;
}
return Status::OK();
diff --git a/src/mongo/db/catalog/collection.h b/src/mongo/db/catalog/collection.h
index 4720c980b9b..b718413e161 100644
--- a/src/mongo/db/catalog/collection.h
+++ b/src/mongo/db/catalog/collection.h
@@ -346,15 +346,12 @@ public:
Status truncate(OperationContext* txn);
/**
- * @param full - does more checks
- * @param scanData - scans each document
* @return OK if the validate run successfully
* OK will be returned even if corruption is found
* deatils will be in result
*/
Status validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateResults* results,
BSONObjBuilder* output);
diff --git a/src/mongo/db/commands/validate.cpp b/src/mongo/db/commands/validate.cpp
index 0037eba6d58..0357a0f7bd3 100644
--- a/src/mongo/db/commands/validate.cpp
+++ b/src/mongo/db/commands/validate.cpp
@@ -37,6 +37,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/query/internal_plans.h"
+#include "mongo/db/storage/record_store.h"
#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
@@ -90,7 +91,15 @@ public:
NamespaceString ns_string(ns);
const bool full = cmdObj["full"].trueValue();
- const bool scanData = full || cmdObj["scandata"].trueValue();
+ const bool scanData = cmdObj["scandata"].trueValue();
+
+ ValidateCmdLevel level = kValidateIndex;
+
+ if (full) {
+ level = kValidateFull;
+ } else if (scanData) {
+ level = kValidateRecordStore;
+ }
if (!ns_string.isNormal() && full) {
errmsg = "Can only run full validate on a regular collection";
@@ -112,7 +121,7 @@ public:
result.append("ns", ns);
ValidateResults results;
- Status status = collection->validate(txn, full, scanData, &results, &result);
+ Status status = collection->validate(txn, level, &results, &result);
if (!status.isOK())
return appendCommandStatus(result, status);
diff --git a/src/mongo/db/index/index_access_method.cpp b/src/mongo/db/index/index_access_method.cpp
index b10bc6add71..e579d13aacb 100644
--- a/src/mongo/db/index/index_access_method.cpp
+++ b/src/mongo/db/index/index_access_method.cpp
@@ -249,11 +249,10 @@ RecordId IndexAccessMethod::findSingle(OperationContext* txn, const BSONObj& key
}
Status IndexAccessMethod::validate(OperationContext* txn,
- bool full,
int64_t* numKeys,
ValidateResults* fullResults) {
long long keys = 0;
- _newInterface->fullValidate(txn, full, &keys, fullResults);
+ _newInterface->fullValidate(txn, &keys, fullResults);
*numKeys = keys;
return Status::OK();
}
diff --git a/src/mongo/db/index/index_access_method.h b/src/mongo/db/index/index_access_method.h
index 1509500d916..0aa16b791fe 100644
--- a/src/mongo/db/index/index_access_method.h
+++ b/src/mongo/db/index/index_access_method.h
@@ -163,19 +163,10 @@ public:
/**
* Walk the entire index, checking the internal structure for consistency.
* Set numKeys to the number of keys in the index.
- *
- * 'output' is used to store results of validate when 'full' is true.
- * If 'full' is false, 'output' may be NULL.
- *
+
* Return OK if the index is valid.
- *
- * Currently wasserts that the index is invalid. This could/should be changed in
- * the future to return a Status.
*/
- Status validate(OperationContext* txn,
- bool full,
- int64_t* numKeys,
- ValidateResults* fullResults);
+ Status validate(OperationContext* txn, int64_t* numKeys, ValidateResults* fullResults);
/**
* Add custom statistics about this index to BSON object builder, for display.
diff --git a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
index 3ff3ae5ce96..65a16414dcc 100644
--- a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
+++ b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
@@ -145,8 +145,7 @@ public:
virtual void temp_cappedTruncateAfter(OperationContext* txn, RecordId end, bool inclusive) {}
virtual Status validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output) {
@@ -209,7 +208,6 @@ public:
}
virtual void fullValidate(OperationContext* txn,
- bool full,
long long* numKeysOut,
ValidateResults* fullResults) const {}
diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp
index b34318b5ab8..a308b6d7984 100644
--- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp
+++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_btree_impl.cpp
@@ -189,7 +189,6 @@ public:
}
virtual void fullValidate(OperationContext* txn,
- bool full,
long long* numKeysOut,
ValidateResults* fullResults) const {
// TODO check invariants?
diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp
index 7fa168612b0..ddee4d85a75 100644
--- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp
+++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.cpp
@@ -525,18 +525,17 @@ void EphemeralForTestRecordStore::temp_cappedTruncateAfter(OperationContext* txn
}
Status EphemeralForTestRecordStore::validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output) {
results->valid = true;
- if (scanData && full) {
+ if (level == kValidateFull) {
for (Records::const_iterator it = _data->records.begin(); it != _data->records.end();
++it) {
const EphemeralForTestRecord& rec = it->second;
size_t dataSize;
- const Status status = adaptor->validate(rec.toRecordData(), &dataSize);
+ const Status status = adaptor->validate(it->first, rec.toRecordData(), &dataSize);
if (!status.isOK()) {
results->valid = false;
results->errors.push_back("invalid object detected (see logs)");
diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h
index 8f83cfe9dfd..8df67ece618 100644
--- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h
+++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h
@@ -92,8 +92,7 @@ public:
virtual void temp_cappedTruncateAfter(OperationContext* txn, RecordId end, bool inclusive);
virtual Status validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output);
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 cea3a8b4d91..cdb1bf05583 100644
--- a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
+++ b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
@@ -96,7 +96,6 @@ public:
}
virtual void fullValidate(OperationContext* txn,
- bool full,
long long* numKeysOut,
ValidateResults* fullResults) const {
*numKeysOut = _btree->fullValidate(txn, NULL, false, false, 0);
diff --git a/src/mongo/db/storage/mmap_v1/heap_record_store_btree.h b/src/mongo/db/storage/mmap_v1/heap_record_store_btree.h
index 4e984a4469d..32dec97358e 100644
--- a/src/mongo/db/storage/mmap_v1/heap_record_store_btree.h
+++ b/src/mongo/db/storage/mmap_v1/heap_record_store_btree.h
@@ -114,8 +114,7 @@ public:
}
virtual Status validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output) {
diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp b/src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp
index f5089ef787f..790f35e98c2 100644
--- a/src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp
+++ b/src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp
@@ -539,8 +539,7 @@ void RecordStoreV1Base::increaseStorageSize(OperationContext* txn, int size, boo
}
Status RecordStoreV1Base::validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output) {
@@ -591,7 +590,7 @@ Status RecordStoreV1Base::validate(OperationContext* txn,
extentDiskLoc = _details->firstExtent(txn);
while (!extentDiskLoc.isNull()) {
Extent* thisExtent = _getExtent(txn, extentDiskLoc);
- if (full) {
+ if (level == kValidateFull) {
extentData << thisExtent->dump();
}
if (!thisExtent->validates(extentDiskLoc, &results->errors)) {
@@ -628,7 +627,7 @@ Status RecordStoreV1Base::validate(OperationContext* txn,
}
output->append("extentCount", extentCount);
- if (full)
+ if (level == kValidateFull)
output->appendArray("extents", extentData.arr());
}
@@ -678,7 +677,7 @@ Status RecordStoreV1Base::validate(OperationContext* txn,
// 4444444444444444444444444
set<DiskLoc> recs;
- if (scanData) {
+ if (level == kValidateRecordStore || level == kValidateFull) {
int n = 0;
int nInvalid = 0;
long long nQuantizedSize = 0;
@@ -711,9 +710,10 @@ Status RecordStoreV1Base::validate(OperationContext* txn,
++nQuantizedSize;
}
- if (full) {
+ if (level == kValidateFull) {
size_t dataSize = 0;
- const Status status = adaptor->validate(r->toRecordData(), &dataSize);
+ const Status status =
+ adaptor->validate(record->id, r->toRecordData(), &dataSize);
if (!status.isOK()) {
results->valid = false;
if (nInvalid == 0) // only log once;
@@ -736,7 +736,7 @@ Status RecordStoreV1Base::validate(OperationContext* txn,
}
output->append("objectsFound", n);
- if (full) {
+ if (level == kValidateFull) {
output->append("invalidObjects", nInvalid);
}
@@ -744,7 +744,7 @@ Status RecordStoreV1Base::validate(OperationContext* txn,
output->appendNumber("bytesWithHeaders", len);
output->appendNumber("bytesWithoutHeaders", nlen);
- if (full) {
+ if (level == kValidateFull) {
output->appendNumber("bytesBson", bsonLen);
}
} // end scanData
@@ -799,7 +799,7 @@ Status RecordStoreV1Base::validate(OperationContext* txn,
}
output->appendNumber("deletedCount", ndel);
output->appendNumber("deletedSize", delSize);
- if (full) {
+ if (level == kValidateFull) {
output->append("delBucketSizes", delBucketSizes.arr());
}
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 4520256f901..c9ee22a40da 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
@@ -219,8 +219,7 @@ public:
void increaseStorageSize(OperationContext* txn, int size, bool enforceQuota);
virtual Status validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output);
diff --git a/src/mongo/db/storage/record_store.h b/src/mongo/db/storage/record_store.h
index 64f93e50c62..ea0ad7c69b7 100644
--- a/src/mongo/db/storage/record_store.h
+++ b/src/mongo/db/storage/record_store.h
@@ -92,6 +92,13 @@ struct BsonRecord {
const BSONObj* docPtr;
};
+enum ValidateCmdLevel : int {
+ kValidateIndex = 0x01,
+ kValidateRecordStore = 0x02,
+ kValidateFull = 0x03
+};
+
+
/**
* Retrieves Records from a RecordStore.
*
@@ -517,15 +524,12 @@ public:
}
/**
- * @param full - does more checks
- * @param scanData - scans each document
* @return OK if the validate run successfully
* OK will be returned even if corruption is found
* deatils will be in result
*/
virtual Status validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output) = 0;
@@ -613,6 +617,8 @@ class ValidateAdaptor {
public:
virtual ~ValidateAdaptor() {}
- virtual Status validate(const RecordData& recordData, size_t* dataSize) = 0;
+ virtual Status validate(const RecordId& recordId,
+ const RecordData& recordData,
+ size_t* dataSize) = 0;
};
}
diff --git a/src/mongo/db/storage/record_store_test_validate.cpp b/src/mongo/db/storage/record_store_test_validate.cpp
index 71790a376a3..d55d8229526 100644
--- a/src/mongo/db/storage/record_store_test_validate.cpp
+++ b/src/mongo/db/storage/record_store_test_validate.cpp
@@ -42,8 +42,6 @@ namespace mongo {
namespace {
// Verify that calling validate() on an empty collection returns an OK status.
-// When either of `full` or `scanData` are false, the ValidateAdaptor
-// should not be used.
TEST(RecordStoreTestHarness, ValidateEmpty) {
unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper());
unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
@@ -59,12 +57,7 @@ TEST(RecordStoreTestHarness, ValidateEmpty) {
ValidateAdaptorSpy adaptor;
ValidateResults results;
BSONObjBuilder stats;
- ASSERT_OK(rs->validate(opCtx.get(),
- false, // full validate
- false, // scan data
- &adaptor,
- &results,
- &stats));
+ ASSERT_OK(rs->validate(opCtx.get(), kValidateIndex, &adaptor, &results, &stats));
ASSERT(results.valid);
ASSERT(results.errors.empty());
}
@@ -72,8 +65,6 @@ TEST(RecordStoreTestHarness, ValidateEmpty) {
}
// Verify that calling validate() on an empty collection returns an OK status.
-// When either of `full` or `scanData` are false, the ValidateAdaptor
-// should not be used.
TEST(RecordStoreTestHarness, ValidateEmptyAndScanData) {
unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper());
unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
@@ -89,42 +80,7 @@ TEST(RecordStoreTestHarness, ValidateEmptyAndScanData) {
ValidateAdaptorSpy adaptor;
ValidateResults results;
BSONObjBuilder stats;
- ASSERT_OK(rs->validate(opCtx.get(),
- false, // full validate
- true, // scan data
- &adaptor,
- &results,
- &stats));
- ASSERT(results.valid);
- ASSERT(results.errors.empty());
- }
- }
-}
-
-// Verify that calling validate() on an empty collection returns an OK status.
-// When either of `full` or `scanData` are false, the ValidateAdaptor
-// should not be used.
-TEST(RecordStoreTestHarness, FullValidateEmpty) {
- unique_ptr<HarnessHelper> harnessHelper(newHarnessHelper());
- unique_ptr<RecordStore> rs(harnessHelper->newNonCappedRecordStore());
-
- {
- unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
- ASSERT_EQUALS(0, rs->numRecords(opCtx.get()));
- }
-
- {
- unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
- {
- ValidateAdaptorSpy adaptor;
- ValidateResults results;
- BSONObjBuilder stats;
- ASSERT_OK(rs->validate(opCtx.get(),
- true, // full validate
- false, // scan data
- &adaptor,
- &results,
- &stats));
+ ASSERT_OK(rs->validate(opCtx.get(), kValidateRecordStore, &adaptor, &results, &stats));
ASSERT(results.valid);
ASSERT(results.errors.empty());
}
@@ -147,12 +103,7 @@ TEST(RecordStoreTestHarness, FullValidateEmptyAndScanData) {
ValidateAdaptorSpy adaptor;
ValidateResults results;
BSONObjBuilder stats;
- ASSERT_OK(rs->validate(opCtx.get(),
- true, // full validate
- true, // scan data
- &adaptor,
- &results,
- &stats));
+ ASSERT_OK(rs->validate(opCtx.get(), kValidateFull, &adaptor, &results, &stats));
ASSERT(results.valid);
ASSERT(results.errors.empty());
}
@@ -160,8 +111,7 @@ TEST(RecordStoreTestHarness, FullValidateEmptyAndScanData) {
}
// Insert multiple records, and verify that calling validate() on a nonempty collection
-// returns an OK status. When either of `full` or `scanData` are false, the ValidateAdaptor
-// should not be used.
+// returns an OK status.
TEST_F(ValidateTest, ValidateNonEmpty) {
{
unique_ptr<OperationContext> opCtx(newOperationContext());
@@ -169,12 +119,8 @@ TEST_F(ValidateTest, ValidateNonEmpty) {
ValidateAdaptorSpy adaptor;
ValidateResults results;
BSONObjBuilder stats;
- ASSERT_OK(getRecordStore().validate(opCtx.get(),
- false, // full validate
- false, // scan data
- &adaptor,
- &results,
- &stats));
+ ASSERT_OK(
+ getRecordStore().validate(opCtx.get(), kValidateIndex, &adaptor, &results, &stats));
ASSERT(results.valid);
ASSERT(results.errors.empty());
}
@@ -182,8 +128,7 @@ TEST_F(ValidateTest, ValidateNonEmpty) {
}
// Insert multiple records, and verify that calling validate() on a nonempty collection
-// returns an OK status. When either of `full` or `scanData` are false, the ValidateAdaptor
-// should not be used.
+// returns an OK status.
TEST_F(ValidateTest, ValidateAndScanDataNonEmpty) {
{
unique_ptr<OperationContext> opCtx(newOperationContext());
@@ -191,34 +136,8 @@ TEST_F(ValidateTest, ValidateAndScanDataNonEmpty) {
ValidateAdaptorSpy adaptor;
ValidateResults results;
BSONObjBuilder stats;
- ASSERT_OK(getRecordStore().validate(opCtx.get(),
- false, // full validate
- true, // scan data
- &adaptor,
- &results,
- &stats));
- ASSERT(results.valid);
- ASSERT(results.errors.empty());
- }
- }
-}
-
-// Insert multiple records, and verify that calling validate() on a nonempty collection
-// returns an OK status. When either of `full` or `scanData` are false, the ValidateAdaptor
-// should not be used.
-TEST_F(ValidateTest, FullValidateNonEmpty) {
- {
- unique_ptr<OperationContext> opCtx(newOperationContext());
- {
- ValidateAdaptorSpy adaptor;
- ValidateResults results;
- BSONObjBuilder stats;
- ASSERT_OK(getRecordStore().validate(opCtx.get(),
- true, // full validate
- false, // scan data
- &adaptor,
- &results,
- &stats));
+ ASSERT_OK(getRecordStore().validate(
+ opCtx.get(), kValidateRecordStore, &adaptor, &results, &stats));
ASSERT(results.valid);
ASSERT(results.errors.empty());
}
@@ -234,12 +153,8 @@ TEST_F(ValidateTest, FullValidateNonEmptyAndScanData) {
ValidateAdaptorSpy adaptor(getInsertedRecords());
ValidateResults results;
BSONObjBuilder stats;
- ASSERT_OK(getRecordStore().validate(opCtx.get(),
- true, // full validate
- true, // scan data
- &adaptor,
- &results,
- &stats));
+ ASSERT_OK(
+ getRecordStore().validate(opCtx.get(), kValidateFull, &adaptor, &results, &stats));
ASSERT(adaptor.allValidated());
ASSERT(results.valid);
ASSERT(results.errors.empty());
diff --git a/src/mongo/db/storage/record_store_test_validate.h b/src/mongo/db/storage/record_store_test_validate.h
index b5a73e041fa..3ef5bcdd368 100644
--- a/src/mongo/db/storage/record_store_test_validate.h
+++ b/src/mongo/db/storage/record_store_test_validate.h
@@ -47,7 +47,7 @@ public:
~ValidateAdaptorSpy() {}
- Status validate(const RecordData& recordData, size_t* dataSize) {
+ Status validate(const RecordId& recordId, const RecordData& recordData, size_t* dataSize) {
std::string s(recordData.data());
ASSERT(1 == _remain.erase(s));
diff --git a/src/mongo/db/storage/sorted_data_interface.h b/src/mongo/db/storage/sorted_data_interface.h
index eeefb10d5ea..d21fb6e3c8f 100644
--- a/src/mongo/db/storage/sorted_data_interface.h
+++ b/src/mongo/db/storage/sorted_data_interface.h
@@ -135,13 +135,9 @@ public:
//
/**
- * 'output' is used to store results of validate when 'full' is true.
- * If 'full' is false, 'output' may be NULL.
- *
* TODO: expose full set of args for testing?
*/
virtual void fullValidate(OperationContext* txn,
- bool full,
long long* numKeysOut,
ValidateResults* fullResults) const = 0;
@@ -185,7 +181,7 @@ public:
*/
virtual long long numEntries(OperationContext* txn) const {
long long x = -1;
- fullValidate(txn, false, &x, NULL);
+ fullValidate(txn, &x, NULL);
return x;
}
diff --git a/src/mongo/db/storage/sorted_data_interface_test_fullvalidate.cpp b/src/mongo/db/storage/sorted_data_interface_test_fullvalidate.cpp
index 6f1b4575a09..98bb9936da9 100644
--- a/src/mongo/db/storage/sorted_data_interface_test_fullvalidate.cpp
+++ b/src/mongo/db/storage/sorted_data_interface_test_fullvalidate.cpp
@@ -68,7 +68,7 @@ TEST(SortedDataInterface, FullValidate) {
{
long long numKeysOut;
const std::unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
- sorted->fullValidate(opCtx.get(), false, &numKeysOut, NULL);
+ sorted->fullValidate(opCtx.get(), &numKeysOut, NULL);
// fullValidate() can set numKeysOut as the number of existing keys or -1.
ASSERT(numKeysOut == nToInsert || numKeysOut == -1);
}
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 e184b88382f..8506af3e43e 100644
--- a/src/mongo/db/storage/sorted_data_interface_test_harness.cpp
+++ b/src/mongo/db/storage/sorted_data_interface_test_harness.cpp
@@ -94,7 +94,7 @@ TEST(SortedDataInterface, InsertWithDups1) {
ASSERT_EQUALS(2, sorted->numEntries(opCtx.get()));
long long x = 0;
- sorted->fullValidate(opCtx.get(), false, &x, NULL);
+ sorted->fullValidate(opCtx.get(), &x, NULL);
ASSERT_EQUALS(2, x);
}
}
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
index 283eca55364..c0b04a41759 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
@@ -266,7 +266,6 @@ void WiredTigerIndex::unindex(OperationContext* txn,
}
void WiredTigerIndex::fullValidate(OperationContext* txn,
- bool full,
long long* numKeysOut,
ValidateResults* fullResults) const {
if (fullResults && !WiredTigerRecoveryUnit::get(txn)->getSessionCache()->isEphemeral()) {
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.h b/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
index f989f0b1ce5..c2d6c70cf12 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
@@ -91,7 +91,6 @@ public:
bool dupsAllowed);
virtual void fullValidate(OperationContext* txn,
- bool full,
long long* numKeysOut,
ValidateResults* fullResults) const;
virtual bool appendCustomStats(OperationContext* txn,
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
index 636f2d86ffd..87fd30bb27d 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
@@ -1494,8 +1494,7 @@ Status WiredTigerRecordStore::compact(OperationContext* txn,
}
Status WiredTigerRecordStore::validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output) {
@@ -1528,9 +1527,9 @@ Status WiredTigerRecordStore::validate(OperationContext* txn,
++nrecords;
auto dataSize = record->data.size();
dataSizeTotal += dataSize;
- if (full && scanData) {
+ if (level == kValidateFull) {
size_t validatedSize;
- Status status = adaptor->validate(record->data, &validatedSize);
+ Status status = adaptor->validate(record->id, record->data, &validatedSize);
// The validatedSize equals dataSize below is not a general requirement, but must be
// true for WT today because we never pad records.
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
index 9e7cc01f276..3ef4626d206 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
@@ -170,8 +170,7 @@ public:
CompactStats* stats);
virtual Status validate(OperationContext* txn,
- bool full,
- bool scanData,
+ ValidateCmdLevel level,
ValidateAdaptor* adaptor,
ValidateResults* results,
BSONObjBuilder* output);
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 e09ccdf3e65..37d64e8bd00 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp
@@ -386,7 +386,7 @@ namespace {
class GoodValidateAdaptor : public ValidateAdaptor {
public:
- virtual Status validate(const RecordData& record, size_t* dataSize) {
+ virtual Status validate(const RecordId& recordId, const RecordData& record, size_t* dataSize) {
*dataSize = static_cast<size_t>(record.size());
return Status::OK();
}
@@ -394,7 +394,7 @@ public:
class BadValidateAdaptor : public ValidateAdaptor {
public:
- virtual Status validate(const RecordData& record, size_t* dataSize) {
+ virtual Status validate(const RecordId& recordId, const RecordData& record, size_t* dataSize) {
*dataSize = static_cast<size_t>(record.size());
return Status(ErrorCodes::UnknownError, "");
}
@@ -463,7 +463,7 @@ TEST_F(SizeStorerValidateTest, Basic) {
unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
ValidateResults results;
BSONObjBuilder output;
- ASSERT_OK(rs->validate(opCtx.get(), false, false, NULL, &results, &output));
+ ASSERT_OK(rs->validate(opCtx.get(), kValidateIndex, NULL, &results, &output));
BSONObj obj = output.obj();
ASSERT_EQUALS(expectedNumRecords, obj.getIntField("nrecords"));
ASSERT_EQUALS(expectedNumRecords, getNumRecords());
@@ -476,20 +476,31 @@ TEST_F(SizeStorerValidateTest, FullWithGoodAdaptor) {
GoodValidateAdaptor adaptor;
ValidateResults results;
BSONObjBuilder output;
- ASSERT_OK(rs->validate(opCtx.get(), true, true, &adaptor, &results, &output));
+ ASSERT_OK(rs->validate(opCtx.get(), kValidateFull, &adaptor, &results, &output));
BSONObj obj = output.obj();
ASSERT_EQUALS(expectedNumRecords, obj.getIntField("nrecords"));
ASSERT_EQUALS(expectedNumRecords, getNumRecords());
ASSERT_EQUALS(expectedDataSize, getDataSize());
}
+// Basic validation does not use the validation adaptor. So passing a bad adaptor
+// should not cause validate to fail.
+TEST_F(SizeStorerValidateTest, BasicWithBadAdapter) {
+ unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
+ BadValidateAdaptor adaptor;
+ ValidateResults results;
+ BSONObjBuilder output;
+ ASSERT_OK(rs->validate(opCtx.get(), kValidateIndex, &adaptor, &results, &output));
+ ASSERT_EQUALS(true, results.valid);
+}
+
// Full validation with a validation adaptor that fails - size storer data is not updated.
TEST_F(SizeStorerValidateTest, FullWithBadAdapter) {
unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
BadValidateAdaptor adaptor;
ValidateResults results;
BSONObjBuilder output;
- ASSERT_OK(rs->validate(opCtx.get(), true, true, &adaptor, &results, &output));
+ ASSERT_OK(rs->validate(opCtx.get(), kValidateFull, &adaptor, &results, &output));
BSONObj obj = output.obj();
ASSERT_EQUALS(expectedNumRecords, obj.getIntField("nrecords"));
ASSERT_EQUALS(0, getNumRecords());
@@ -519,7 +530,7 @@ TEST_F(SizeStorerValidateTest, InvalidSizeStorerAtCreation) {
GoodValidateAdaptor adaptor;
ValidateResults results;
BSONObjBuilder output;
- ASSERT_OK(rs->validate(opCtx.get(), true, true, &adaptor, &results, &output));
+ ASSERT_OK(rs->validate(opCtx.get(), kValidateFull, &adaptor, &results, &output));
BSONObj obj = output.obj();
ASSERT_EQUALS(expectedNumRecords, obj.getIntField("nrecords"));
ASSERT_EQUALS(expectedNumRecords, getNumRecords());
diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp
index 308c81482d1..1c32e715510 100644
--- a/src/mongo/dbtests/validate_tests.cpp
+++ b/src/mongo/dbtests/validate_tests.cpp
@@ -70,7 +70,8 @@ protected:
bool checkValid() {
ValidateResults results;
BSONObjBuilder output;
- ASSERT_OK(collection()->validate(&_txn, _full, false, &results, &output));
+ ASSERT_OK(collection()->validate(
+ &_txn, _full ? kValidateFull : kValidateIndex, &results, &output));
// Check if errors are reported if and only if valid is set to false.
ASSERT_EQ(results.valid, results.errors.empty());