summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/auth/lib/commands_lib.js16
-rw-r--r--jstests/core/fsync.js11
-rw-r--r--jstests/noPassthroughWithMongod/fsync2.js2
-rw-r--r--jstests/repl/snapshot1.js4
-rw-r--r--jstests/replsets/fsync_lock_read_secondaries.js2
-rw-r--r--jstests/replsets/maxSyncSourceLagSecs.js2
-rw-r--r--jstests/replsets/stepdown.js2
-rw-r--r--jstests/replsets/stepdown3.js2
-rw-r--r--src/mongo/db/commands/fsync.cpp163
-rw-r--r--src/mongo/db/commands/fsync.h2
-rw-r--r--src/mongo/db/instance.cpp66
-rw-r--r--src/mongo/shell/db.js9
12 files changed, 201 insertions, 80 deletions
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js
index c73439d2494..e4d5fbed16e 100644
--- a/jstests/auth/lib/commands_lib.js
+++ b/jstests/auth/lib/commands_lib.js
@@ -1144,6 +1144,22 @@ var authCommandsLib = {
]
},
{
+ testname: "fsyncUnlock",
+ command: {fsyncUnlock: 1},
+ testcases: [
+ {
+ runOnDb: adminDbName,
+ roles: roles_hostManager,
+ privileges: [
+ { resource: {cluster: true}, actions: ["fsync"] }
+ ],
+ expectFail: true
+ },
+ { runOnDb: firstDbName, roles: {} },
+ { runOnDb: secondDbName, roles: {} }
+ ]
+ },
+ {
testname: "geoNear",
command: {geoNear: "x", near: [50, 50], num: 1},
setup: function (db) {
diff --git a/jstests/core/fsync.js b/jstests/core/fsync.js
index 53ebef0f97d..14c5d323591 100644
--- a/jstests/core/fsync.js
+++ b/jstests/core/fsync.js
@@ -36,3 +36,14 @@ assert.eq(2, fsyncLockDB.coll.count({}));
// Ensure eval is not allowed to invoke fsyncLock
assert(!db.eval('db.fsyncLock()').ok, "eval('db.fsyncLock()') should fail.");
+
+// Check that the fsyncUnlock pseudo-command (a lookup on cmd.$sys.unlock)
+// still has the same effect as a legitimate 'fsyncUnlock' command
+// TODO: remove this in in the release following MongoDB 3.2 when pseudo-commands
+// are removed
+var fsyncCommandRes = db.fsyncLock();
+assert(fsyncLockRes.ok, "fsyncLock command failed against admin DB");
+assert(db.currentOp().fsyncLock, "Value in db.currentOp incorrect for fsyncLocked server");
+var fsyncPseudoCommandRes = db.getSiblingDB("admin").$cmd.sys.unlock.findOne();
+assert(fsyncPseudoCommandRes.ok, "fsyncUnlock pseudo-command failed");
+assert(db.currentOp().fsyncLock == null, "fsyncUnlock is not null in db.currentOp");
diff --git a/jstests/noPassthroughWithMongod/fsync2.js b/jstests/noPassthroughWithMongod/fsync2.js
index 7080837a99b..fcf4da08533 100644
--- a/jstests/noPassthroughWithMongod/fsync2.js
+++ b/jstests/noPassthroughWithMongod/fsync2.js
@@ -40,7 +40,7 @@ function doTest() {
// Uncomment once SERVER-4243 is fixed
//assert.eq(1, m.getDB(db.getName()).fsync2.count());
- assert( m.getDB("admin").$cmd.sys.unlock.findOne().ok );
+ assert( m.getDB("admin").fsyncUnlock().ok );
assert.eq( 2, db.fsync2.count() );
diff --git a/jstests/repl/snapshot1.js b/jstests/repl/snapshot1.js
index 3be37aa125b..076ec2403f9 100644
--- a/jstests/repl/snapshot1.js
+++ b/jstests/repl/snapshot1.js
@@ -14,7 +14,7 @@ for( i = 0; i < 1000; ++i )
m.getDB( "admin" ).runCommand( {fsync:1,lock:1} );
copyDbpath( rt1.getPath( true ), rt1.getPath( false ) );
-m.getDB( "admin" ).$cmd.sys.unlock.findOne();
+m.getDB( "admin" ).fsyncUnlock();
s1 = rt1.start( false, null, true );
assert.eq( 1000, s1.getDB( baseName )[ baseName ].count() );
@@ -23,7 +23,7 @@ assert.soon( function() { return 1001 == s1.getDB( baseName )[ baseName ].count(
s1.getDB( "admin" ).runCommand( {fsync:1,lock:1} );
copyDbpath( rt1.getPath( false ), rt2.getPath( false ) );
-s1.getDB( "admin" ).$cmd.sys.unlock.findOne();
+s1.getDB( "admin" ).fsyncUnlock();
s2 = rt2.start( false, null, true );
assert.eq( 1001, s2.getDB( baseName )[ baseName ].count() );
diff --git a/jstests/replsets/fsync_lock_read_secondaries.js b/jstests/replsets/fsync_lock_read_secondaries.js
index 7aab61fd4b2..d217e85241f 100644
--- a/jstests/replsets/fsync_lock_read_secondaries.js
+++ b/jstests/replsets/fsync_lock_read_secondaries.js
@@ -53,7 +53,7 @@ for (var i=0; i<docNum; i++) {
// this should work just fine.
var slave0count = slaves[0].getDB("foo").bar.count();
assert.eq(slave0count, 100, "Doc count in fsync lock wrong. Expected (=100), found " + slave0count);
-assert(slaves[0].getDB("admin").$cmd.sys.unlock.findOne().ok);
+assert(slaves[0].getDB("admin").fsyncUnlock().ok);
// The secondary should have equal or more documents than what it had before.
assert.soon(function() {
diff --git a/jstests/replsets/maxSyncSourceLagSecs.js b/jstests/replsets/maxSyncSourceLagSecs.js
index 8d4702510e1..1a7348c2e64 100644
--- a/jstests/replsets/maxSyncSourceLagSecs.js
+++ b/jstests/replsets/maxSyncSourceLagSecs.js
@@ -44,6 +44,6 @@
return (slaves[1].getDB("foo").bar.count() === 2);
}, "slave should have caught up after syncing to primary.");
- assert.commandWorked(slaves[0].getDB("admin").$cmd.sys.unlock.findOne());
+ assert.commandWorked(slaves[0].getDB("admin").fsyncUnlock());
replTest.stopSet();
}());
diff --git a/jstests/replsets/stepdown.js b/jstests/replsets/stepdown.js
index 474154ae6ef..275d7726d40 100644
--- a/jstests/replsets/stepdown.js
+++ b/jstests/replsets/stepdown.js
@@ -53,7 +53,7 @@ assert.eq(r2.ismaster, false);
assert.eq(r2.secondary, true);
print("\nunlock");
-printjson(locked.getDB("admin").$cmd.sys.unlock.findOne());
+printjson(locked.getDB("admin").fsyncUnlock());
print("\nreset stepped down time");
master.getDB("admin").runCommand({replSetFreeze:0});
diff --git a/jstests/replsets/stepdown3.js b/jstests/replsets/stepdown3.js
index af7f84607b7..05ce573c883 100644
--- a/jstests/replsets/stepdown3.js
+++ b/jstests/replsets/stepdown3.js
@@ -46,5 +46,5 @@ print("result of gle:");
printjson(result);
// unlock and shut down
-printjson(locked.getDB("admin").$cmd.sys.unlock.findOne());
+printjson(locked.getDB("admin").fsyncUnlock());
replTest.stopSet();
diff --git a/src/mongo/db/commands/fsync.cpp b/src/mongo/db/commands/fsync.cpp
index f9f829df3b7..b6fde72e309 100644
--- a/src/mongo/db/commands/fsync.cpp
+++ b/src/mongo/db/commands/fsync.cpp
@@ -26,16 +26,24 @@
* it in the license file.
*/
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
#include "mongo/db/commands/fsync.h"
+#include <iostream>
+#include <memory>
+#include <sstream>
#include <string>
#include <vector>
+#include "mongo/base/init.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/audit.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_manager.h"
+#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/commands.h"
@@ -43,12 +51,11 @@
#include "mongo/db/storage/mmap_v1/dur.h"
#include "mongo/db/storage/storage_engine.h"
#include "mongo/db/client.h"
-#include "mongo/db/jsobj.h"
#include "mongo/db/operation_context_impl.h"
+#include "mongo/stdx/memory.h"
#include "mongo/util/background.h"
#include "mongo/util/log.h"
-
namespace mongo {
using std::endl;
@@ -152,7 +159,73 @@ namespace mongo {
}
return 1;
}
- } fsyncCmd;
+ };
+
+ namespace {
+ bool unlockFsync();
+ } // namespace
+
+ class FSyncUnlockCommand : public Command {
+ public:
+
+ FSyncUnlockCommand() : Command("fsyncUnlock") {}
+
+ bool isWriteCommandForConfigServer() const override { return false; }
+
+ bool slaveOk() const override { return true; }
+
+ bool adminOnly() const override { return true; }
+
+ Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) override {
+
+ bool isAuthorized = client->getAuthorizationSession()->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(),
+ ActionType::unlock);
+
+ if (isAuthorized) {
+ audit::logFsyncUnlockAuthzCheck(client, ErrorCodes::OK);
+ return Status::OK();
+ }
+ else {
+ audit::logFsyncUnlockAuthzCheck(client, ErrorCodes::Unauthorized);
+ return Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+ }
+
+ bool run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) override {
+
+ log() << "command: unlock requested";
+
+ if (unlockFsync()) {
+ result.append("info", "unlock completed");
+ return true;
+ }
+ else {
+ errmsg = "not locked";
+ return false;
+ }
+ }
+
+ };
+
+ namespace {
+ std::unique_ptr<FSyncCommand> fsyncCmd;
+ std::unique_ptr<FSyncUnlockCommand> fsyncUnlockCmd;
+ } // namespace
+
+ MONGO_INITIALIZER(FSyncAndFsyncUnlock)(InitializerContext*) {
+ fsyncCmd = stdx::make_unique<FSyncCommand>();
+ fsyncUnlockCmd = stdx::make_unique<FSyncUnlockCommand>();
+ return Status::OK();
+ }
SimpleMutex filesLockedFsync("filesLockedFsync");
@@ -163,17 +236,17 @@ namespace mongo {
ScopedTransaction transaction(&txn, MODE_X);
Lock::GlobalWrite global(txn.lockState()); // No WriteUnitOfWork needed
- SimpleMutex::scoped_lock lk(fsyncCmd.m);
-
- invariant(!fsyncCmd.locked); // impossible to get here if locked is true
- try {
+ SimpleMutex::scoped_lock lk(fsyncCmd->m);
+
+ invariant(!fsyncCmd->locked); // impossible to get here if locked is true
+ try {
getDur().syncDataAndTruncateJournal(&txn);
- }
- catch( std::exception& e ) {
+ }
+ catch( std::exception& e ) {
error() << "error doing syncDataAndTruncateJournal: " << e.what() << endl;
- fsyncCmd.err = e.what();
- fsyncCmd._threadSync.notify_one();
- fsyncCmd.locked = false;
+ fsyncCmd->err = e.what();
+ fsyncCmd->_threadSync.notify_one();
+ fsyncCmd->locked = false;
return;
}
@@ -183,47 +256,49 @@ namespace mongo {
StorageEngine* storageEngine = getGlobalEnvironment()->getGlobalStorageEngine();
storageEngine->flushAllFiles(true);
}
- catch( std::exception& e ) {
+ catch( std::exception& e ) {
error() << "error doing flushAll: " << e.what() << endl;
- fsyncCmd.err = e.what();
- fsyncCmd._threadSync.notify_one();
- fsyncCmd.locked = false;
+ fsyncCmd->err = e.what();
+ fsyncCmd->_threadSync.notify_one();
+ fsyncCmd->locked = false;
return;
}
- invariant(!fsyncCmd.locked);
- fsyncCmd.locked = true;
-
- fsyncCmd._threadSync.notify_one();
+ invariant(!fsyncCmd->locked);
+ fsyncCmd->locked = true;
+
+ fsyncCmd->_threadSync.notify_one();
- while ( ! fsyncCmd.pendingUnlock ) {
- fsyncCmd._unlockSync.wait(fsyncCmd.m);
+ while ( ! fsyncCmd->pendingUnlock ) {
+ fsyncCmd->_unlockSync.wait(fsyncCmd->m);
}
- fsyncCmd.pendingUnlock = false;
-
- fsyncCmd.locked = false;
- fsyncCmd.err = "unlocked";
+ fsyncCmd->pendingUnlock = false;
- fsyncCmd._unlockSync.notify_one();
+ fsyncCmd->locked = false;
+ fsyncCmd->err = "unlocked";
+
+ fsyncCmd->_unlockSync.notify_one();
}
- bool lockedForWriting() {
- return fsyncCmd.locked;
+ bool lockedForWriting() {
+ return fsyncCmd->locked;
}
- // @return true if unlocked
- bool _unlockFsync() {
- SimpleMutex::scoped_lock lk( fsyncCmd.m );
- if( !fsyncCmd.locked ) {
- return false;
- }
- fsyncCmd.pendingUnlock = true;
- fsyncCmd._unlockSync.notify_one();
- fsyncCmd._threadSync.notify_one();
-
- while ( fsyncCmd.locked ) {
- fsyncCmd._unlockSync.wait( fsyncCmd.m );
+ namespace {
+ // @return true if unlocked
+ bool unlockFsync() {
+ SimpleMutex::scoped_lock lk( fsyncCmd->m );
+ if( !fsyncCmd->locked ) {
+ return false;
+ }
+ fsyncCmd->pendingUnlock = true;
+ fsyncCmd->_unlockSync.notify_one();
+ fsyncCmd->_threadSync.notify_one();
+
+ while ( fsyncCmd->locked ) {
+ fsyncCmd->_unlockSync.wait( fsyncCmd->m );
+ }
+ return true;
}
- return true;
- }
+ } // namespace
}
diff --git a/src/mongo/db/commands/fsync.h b/src/mongo/db/commands/fsync.h
index f3fa2adb993..4072a1f6e50 100644
--- a/src/mongo/db/commands/fsync.h
+++ b/src/mongo/db/commands/fsync.h
@@ -34,4 +34,4 @@ namespace mongo {
// Use this for blocking during an fsync-and-lock
extern SimpleMutex filesLockedFsync;
bool lockedForWriting();
-}
+} // namespace mongo
diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp
index 46e0edd4b13..242a81db415 100644
--- a/src/mongo/db/instance.cpp
+++ b/src/mongo/db/instance.cpp
@@ -180,32 +180,6 @@ namespace mongo {
replyToQuery(0, m, dbresponse, obj);
}
- bool _unlockFsync();
- static void unlockFsync(OperationContext* txn, const char *ns, Message& m, DbResponse &dbresponse) {
- BSONObj obj;
-
- const bool isAuthorized = txn->getClient()->getAuthorizationSession()->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::unlock);
- audit::logFsyncUnlockAuthzCheck(
- txn->getClient(), isAuthorized ? ErrorCodes::OK : ErrorCodes::Unauthorized);
- if (!isAuthorized) {
- obj = fromjson("{\"err\":\"unauthorized\"}");
- }
- else if (strncmp(ns, "admin.", 6) != 0 ) {
- obj = fromjson("{\"err\":\"unauthorized - this command must be run against the admin DB\"}");
- }
- else {
- log() << "command: unlock requested" << endl;
- if( _unlockFsync() ) {
- obj = fromjson("{ok:1,\"info\":\"unlock completed\"}");
- }
- else {
- obj = fromjson("{ok:0,\"errmsg\":\"not locked\"}");
- }
- }
- replyToQuery(0, m, dbresponse, obj);
- }
-
namespace {
void generateErrorResponse(const AssertionException* exception,
@@ -324,6 +298,44 @@ namespace {
dbResponse.responseTo = responseTo;
}
+namespace {
+
+ // In SERVER-7775 we reimplemented the pseudo-commands fsyncUnlock, inProg, and killOp
+ // as ordinary commands. To support old clients for another release, this helper serves
+ // to execute the real command from the legacy pseudo-command codepath.
+ // TODO: remove after MongoDB 3.2 is released
+ void receivedPseudoCommand(OperationContext* txn,
+ const NamespaceString& nss,
+ Client& client,
+ DbResponse& dbResponse,
+ Message& message,
+ StringData realCommandName) {
+ Message interposed;
+
+ NamespaceString interposedNss(nss.db(), "$cmd");
+
+ BSONObjBuilder cmdBob;
+ cmdBob.append(realCommandName, 1);
+ auto cmd = cmdBob.done();
+
+ // TODO: use OP_COMMAND here instead of constructing
+ // a legacy OP_QUERY style command
+ BufBuilder cmdMsgBuf;
+ cmdMsgBuf.appendNum(DataView(message.header().data()).readLE<int32_t>()); // flags
+ cmdMsgBuf.appendStr(interposedNss.db(), false); // not including null byte
+ cmdMsgBuf.appendStr(".$cmd");
+ cmdMsgBuf.appendNum(0); // ntoskip
+ cmdMsgBuf.appendNum(1); // ntoreturn
+ cmdMsgBuf.appendBuf(cmd.objdata(), cmd.objsize());
+
+ interposed.setData(dbQuery, cmdMsgBuf.buf(), cmdMsgBuf.len());
+ interposed.header().setId(message.header().getId());
+
+ receivedCommand(txn, interposedNss, client, dbResponse, interposed);
+ }
+
+} // namespace
+
static void receivedQuery(OperationContext* txn,
const NamespaceString& nss,
Client& c,
@@ -408,7 +420,7 @@ namespace {
return;
}
if (nsString.coll() == "$cmd.sys.unlock") {
- unlockFsync(txn, ns, m, dbresponse);
+ receivedPseudoCommand(txn, nsString, c, dbresponse, m, "fsyncUnlock");
return;
}
}
diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js
index 48e086ad9f6..9a6c5399067 100644
--- a/src/mongo/shell/db.js
+++ b/src/mongo/shell/db.js
@@ -975,7 +975,14 @@ DB.prototype.fsyncLock = function() {
}
DB.prototype.fsyncUnlock = function() {
- return this.getSiblingDB("admin").$cmd.sys.unlock.findOne()
+ var res = this.adminCommand({fsyncUnlock: 1});
+ if (!res.ok &&
+ // handle both error messages for nonexistent command...
+ (res.errmsg.startsWith("no such cmd") || res.errmsg.startsWith("no such command"))) {
+ // fallback for old servers
+ res = this.getSiblingDB("admin").$cmd.sys.unlock.findOne();
+ }
+ return res;
}
DB.autocomplete = function(obj){