summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2017-08-01 21:24:45 -0400
committerGregory Wlodarek <gregory.wlodarek@mongodb.com>2017-08-01 21:24:45 -0400
commitc99aaa0d4152783f533883ebf311a947b7854d3d (patch)
treeb55234979a536a80ae431e49a732468b32407a51
parent9be0ac031fd016f768297b3647a4b786aa7ae5b6 (diff)
downloadmongo-c99aaa0d4152783f533883ebf311a947b7854d3d.tar.gz
SERVER-30351 Add a traverseRecordStore() function in the RecordStoreValidateAdaptor
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp20
-rw-r--r--src/mongo/db/catalog/private/record_store_validate_adaptor.cpp55
-rw-r--r--src/mongo/db/catalog/private/record_store_validate_adaptor.h9
3 files changed, 77 insertions, 7 deletions
diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp
index c229698b205..b4780bd66a7 100644
--- a/src/mongo/db/catalog/collection_impl.cpp
+++ b/src/mongo/db/catalog/collection_impl.cpp
@@ -1007,16 +1007,21 @@ using ValidateResultsMap = std::map<std::string, ValidateResults>;
void _validateRecordStore(OperationContext* opCtx,
RecordStore* recordStore,
ValidateCmdLevel level,
+ bool background,
RecordStoreValidateAdaptor* indexValidator,
ValidateResults* results,
BSONObjBuilder* output) {
- // Validate RecordStore and, if `level == kValidateFull`, cross validate indexes and
- // RecordStore.
- auto status = recordStore->validate(opCtx, level, indexValidator, results, output);
- // RecordStore::validate always returns Status::OK(). Errors are reported through
- // `results`.
- dassert(status.isOK());
+ // Validate RecordStore and, if `level == kValidateFull`, use the RecordStore's validate
+ // function.
+ if (background) {
+ indexValidator->traverseRecordStore(recordStore, level, results, output);
+ } else {
+ auto status = recordStore->validate(opCtx, level, indexValidator, results, output);
+ // RecordStore::validate always returns Status::OK(). Errors are reported through
+ // `results`.
+ dassert(status.isOK());
+ }
}
void _validateIndexes(OperationContext* opCtx,
@@ -1165,7 +1170,8 @@ Status CollectionImpl::validate(OperationContext* opCtx,
// Validate the record store
log(LogComponent::kIndex) << "validating collection " << ns().toString() << endl;
- _validateRecordStore(opCtx, _recordStore, level, &indexValidator, results, output);
+ _validateRecordStore(
+ opCtx, _recordStore, level, /*somgarg*/ false, &indexValidator, results, output);
// Validate indexes and check for mismatches.
if (results->valid) {
diff --git a/src/mongo/db/catalog/private/record_store_validate_adaptor.cpp b/src/mongo/db/catalog/private/record_store_validate_adaptor.cpp
index a7f70e96b9f..8b978820b9b 100644
--- a/src/mongo/db/catalog/private/record_store_validate_adaptor.cpp
+++ b/src/mongo/db/catalog/private/record_store_validate_adaptor.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/storage/key_string.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/rpc/object_check.h"
+#include "mongo/util/log.h"
namespace mongo {
@@ -186,6 +187,60 @@ void RecordStoreValidateAdaptor::traverseIndex(const IndexAccessMethod* iam,
*numTraversedKeys = numKeys;
}
+void RecordStoreValidateAdaptor::traverseRecordStore(RecordStore* recordStore,
+ ValidateCmdLevel level,
+ ValidateResults* results,
+ BSONObjBuilder* output) {
+
+ long long nrecords = 0;
+ long long dataSizeTotal = 0;
+ long long nInvalid = 0;
+
+ results->valid = true;
+ std::unique_ptr<SeekableRecordCursor> cursor = recordStore->getCursor(_opCtx, true);
+ int interruptInterval = 4096;
+ RecordId prevRecordId;
+
+ while (auto record = cursor->next()) {
+ ++nrecords;
+
+ if (!(nrecords % interruptInterval)) {
+ _opCtx->checkForInterrupt();
+ }
+
+ auto dataSize = record->data.size();
+ dataSizeTotal += dataSize;
+ size_t validatedSize;
+ Status status = validate(record->id, record->data, &validatedSize);
+
+ // Checks to ensure isInRecordIdOrder() is being used properly.
+ if (prevRecordId.isNormal()) {
+ invariant(prevRecordId < record->id);
+ }
+
+ // While some storage engines, such as MMAPv1, may use padding, we still require
+ // that they return the unpadded record data.
+ if (!status.isOK() || validatedSize != static_cast<size_t>(dataSize)) {
+ if (results->valid) {
+ // Only log once.
+ results->errors.push_back("detected one or more invalid documents (see logs)");
+ }
+ nInvalid++;
+ results->valid = false;
+ log() << "document at location: " << record->id << " is corrupted";
+ }
+
+ prevRecordId = record->id;
+ }
+
+ if (results->valid) {
+ recordStore->updateStatsAfterRepair(_opCtx, nrecords, dataSizeTotal);
+ }
+
+ output->append("nInvalidDocuments", nInvalid);
+ output->appendNumber("nrecords", nrecords);
+}
+
void RecordStoreValidateAdaptor::validateIndexKeyCount(IndexDescriptor* idx,
int64_t numRecs,
ValidateResults& results) {
diff --git a/src/mongo/db/catalog/private/record_store_validate_adaptor.h b/src/mongo/db/catalog/private/record_store_validate_adaptor.h
index cccc4e06254..2cdb21ad429 100644
--- a/src/mongo/db/catalog/private/record_store_validate_adaptor.h
+++ b/src/mongo/db/catalog/private/record_store_validate_adaptor.h
@@ -76,6 +76,15 @@ public:
int64_t* numTraversedKeys);
/**
+ * Traverses the record store to retrieve every record and go through its document key
+ * set to keep track of the index consistency during a validation.
+ */
+ void traverseRecordStore(RecordStore* recordStore,
+ ValidateCmdLevel level,
+ ValidateResults* results,
+ BSONObjBuilder* output);
+
+ /**
* Validate that the number of document keys matches the number of index keys.
*/
void validateIndexKeyCount(IndexDescriptor* idx, int64_t numRecs, ValidateResults& results);