summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Hernandez <scotthernandez@gmail.com>2014-12-30 14:46:29 -0500
committerScott Hernandez <scotthernandez@gmail.com>2015-01-02 14:21:08 -0500
commit8b37507dd51cdf058377a24ca0171e7fae6f2c6b (patch)
tree1bea2528edb8df7cf70fda21f6894e59af820f60
parent430bafbd8643bd1d30513ad231850c6927e8553d (diff)
downloadmongo-8b37507dd51cdf058377a24ca0171e7fae6f2c6b.tar.gz
SERVER-16502: open database after repair is done
-rw-r--r--jstests/core/repair_database.js39
-rw-r--r--src/mongo/db/dbcommands.cpp2
-rw-r--r--src/mongo/db/repair_database.cpp27
-rw-r--r--src/mongo/db/repair_database.h5
4 files changed, 66 insertions, 7 deletions
diff --git a/jstests/core/repair_database.js b/jstests/core/repair_database.js
new file mode 100644
index 00000000000..ca4bddd4bb1
--- /dev/null
+++ b/jstests/core/repair_database.js
@@ -0,0 +1,39 @@
+/**
+ * This tests checks that repair database works and doesn't leave the database in a bad state
+ * 1.) Drop "repairDB" database
+ * 2.) Ensure repair works with single collection with an extra index. and one doc
+ * 3.) Ensure repair works with no docs, same collection/index as above
+ * 4.) Ensure repair works with 2 collections, one doc in each
+ */
+
+// 1. Drop db
+var mydb = db.getSisterDB( "repairDB" );
+mydb.dropDatabase()
+
+var myColl = mydb.a;
+
+// 2
+var doc = {_id:1, a:"hello world"};
+myColl.insert(doc);
+myColl.ensureIndex({a:1})
+mydb.repairDatabase();
+var foundDoc = myColl.findOne();
+
+assert.neq(null, foundDoc)
+assert.eq(1, foundDoc._id)
+
+assert.docEq(doc, myColl.findOne({a:doc.a}))
+assert.docEq(doc, myColl.findOne({_id:1}))
+
+// 3
+var myColl2 = mydb.b;
+myColl.remove({});
+mydb.repairDatabase();
+
+// 4
+var myColl2 = mydb.b;
+myColl.insert(doc);
+myColl2.insert(doc);
+mydb.repairDatabase();
+assert.docEq(doc, myColl.findOne({a:doc.a}))
+assert.docEq(doc, myColl2.findOne({a:doc.a}))
diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp
index a16a16c22e1..9e9d16f4159 100644
--- a/src/mongo/db/dbcommands.cpp
+++ b/src/mongo/db/dbcommands.cpp
@@ -290,6 +290,8 @@ namespace mongo {
IndexBuilder::restoreIndexes(indexesInProg);
+ // Open database before returning
+ dbHolder().openDb(txn, dbname);
return appendCommandStatus( result, status );
}
} cmdRepairDatabase;
diff --git a/src/mongo/db/repair_database.cpp b/src/mongo/db/repair_database.cpp
index af27db45098..8f49e4df300 100644
--- a/src/mongo/db/repair_database.cpp
+++ b/src/mongo/db/repair_database.cpp
@@ -55,7 +55,6 @@ namespace {
DatabaseCatalogEntry* dbce,
const std::string& collectionName) {
- Database db(txn, dbce->name(), dbce);
CollectionCatalogEntry* cce = dbce->getCollectionCatalogEntry(txn, collectionName);
std::vector<string> indexNames;
@@ -81,6 +80,7 @@ namespace {
// Skip the rest if there are no indexes to rebuild.
if (indexSpecs.empty()) return Status::OK();
+ boost::scoped_ptr<Database> db;
Collection* collection;
boost::scoped_ptr<MultiIndexBlock> indexer;
{
@@ -102,7 +102,8 @@ namespace {
// Indexes must be dropped before we open the Collection otherwise we could attempt to
// open a bad index and fail.
// TODO see if MultiIndexBlock can be made to work without a Collection.
- collection = db.getCollection(txn, collectionName);
+ db.reset(new Database(txn, dbce->name(), dbce));
+ collection = db->getCollection(txn, collectionName);
invariant(collection);
indexer.reset(new MultiIndexBlock(txn, collection));
@@ -166,6 +167,8 @@ namespace {
bool preserveClonedFilesOnFailure,
bool backupOriginalFiles) {
+
+
// We must hold some form of lock here
invariant(txn->lockState()->isLocked());
invariant( dbName.find( '.' ) == string::npos );
@@ -192,12 +195,22 @@ namespace {
return Status( ErrorCodes::BadValue, "backupOriginalFiles not supported" );
}
- // Close and reopen the db to invalidate all current users and caches.
- // WARNING: it is important that the opened Database object isn't used for anything as its
- // cache must remain empty until we return.
+ // Close the db to invalidate all current users and caches.
dbHolder().close(txn, dbName);
- dbHolder().openDb(txn, dbName);
-
+ // Open the db after everything finishes
+ class OpenDbInDestructor {
+ public:
+ OpenDbInDestructor(OperationContext* txn, const std::string& db) :
+ _dbName(db)
+ , _txn(txn)
+ {}
+ ~OpenDbInDestructor() {
+ dbHolder().openDb(_txn, _dbName);
+ }
+ private:
+ const std::string& _dbName;
+ OperationContext* _txn;
+ } dbOpener(txn, dbName);
DatabaseCatalogEntry* dbce = engine->getDatabaseCatalogEntry(txn, dbName);
std::list<std::string> colls;
diff --git a/src/mongo/db/repair_database.h b/src/mongo/db/repair_database.h
index 85d2729a277..17fb58274aa 100644
--- a/src/mongo/db/repair_database.h
+++ b/src/mongo/db/repair_database.h
@@ -36,6 +36,11 @@ namespace mongo {
class StorageEngine;
class StringData;
+ /**
+ * Repairs a database using a storage engine-specific, best-effort process.
+ * Some data may be lost or modified in the process but the output will
+ * be structurally valid on successful return.
+ */
Status repairDatabase(OperationContext* txn,
StorageEngine* engine,
const std::string& dbName,