summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/service_context_d_test_fixture.cpp2
-rw-r--r--src/mongo/db/storage/control/journal_flusher.cpp12
-rw-r--r--src/mongo/db/storage/control/journal_flusher.h35
-rw-r--r--src/mongo/db/storage/control/storage_control.cpp60
-rw-r--r--src/mongo/db/storage/control/storage_control.h21
5 files changed, 88 insertions, 42 deletions
diff --git a/src/mongo/db/service_context_d_test_fixture.cpp b/src/mongo/db/service_context_d_test_fixture.cpp
index 2740a03d446..dcd3587d98a 100644
--- a/src/mongo/db/service_context_d_test_fixture.cpp
+++ b/src/mongo/db/service_context_d_test_fixture.cpp
@@ -84,7 +84,7 @@ ServiceContextMongoDTest::ServiceContextMongoDTest(std::string engine, RepairAct
initializeStorageEngine(serviceContext,
StorageEngineInitFlags::kAllowNoLockFile |
StorageEngineInitFlags::kSkipMetadataFile);
- StorageControl::startStorageControls(serviceContext);
+ StorageControl::startStorageControls(serviceContext, true /*forTestOnly*/);
DatabaseHolder::set(serviceContext, std::make_unique<DatabaseHolderImpl>());
IndexAccessMethodFactory::set(serviceContext, std::make_unique<IndexAccessMethodFactoryImpl>());
diff --git a/src/mongo/db/storage/control/journal_flusher.cpp b/src/mongo/db/storage/control/journal_flusher.cpp
index c5e8629b055..7a1d532ec93 100644
--- a/src/mongo/db/storage/control/journal_flusher.cpp
+++ b/src/mongo/db/storage/control/journal_flusher.cpp
@@ -117,7 +117,8 @@ void JournalFlusher::run() {
}
// Wait until either journalCommitIntervalMs passes or an immediate journal flush is
- // requested (or shutdown).
+ // requested (or shutdown). If _disablePeriodicFlushes is set, then the thread will not
+ // wake up until a journal flush is externally requested.
auto deadline =
Date_t::now() + Milliseconds(storageGlobalParams.journalCommitIntervalMs.load());
@@ -125,8 +126,13 @@ void JournalFlusher::run() {
stdx::unique_lock<Latch> lk(_stateMutex);
MONGO_IDLE_THREAD_BLOCK;
- _flushJournalNowCV.wait_until(
- lk, deadline.toSystemTimePoint(), [&] { return _flushJournalNow || _shuttingDown; });
+ if (_disablePeriodicFlushes) {
+ _flushJournalNowCV.wait(lk, [&] { return _flushJournalNow || _shuttingDown; });
+ } else {
+ _flushJournalNowCV.wait_until(lk, deadline.toSystemTimePoint(), [&] {
+ return _flushJournalNow || _shuttingDown;
+ });
+ }
_flushJournalNow = false;
diff --git a/src/mongo/db/storage/control/journal_flusher.h b/src/mongo/db/storage/control/journal_flusher.h
index 7204a09104c..f38bd50f2e0 100644
--- a/src/mongo/db/storage/control/journal_flusher.h
+++ b/src/mongo/db/storage/control/journal_flusher.h
@@ -38,9 +38,32 @@ namespace mongo {
class OperationContext;
+/**
+ * A periodic and signalable thread that flushes data to disk. Constructor parameter will dictate
+ * whether to periodically flush or only on signal.
+ *
+ * This thread is helpful for two reasons:
+ * - Periodically flushing data to disk may protect users doing writes with {j: false} from losing
+ * a great deal of their data across a server crash.
+ * - Asynchronously grouping data flush requests reduces the total number of flushes executed,
+ * reducing i/o load on the system and improving write performance. This thread groups both the
+ * periodic flushes and immediate flush requests from the rest of the system.
+ *
+ * And incidentally helpful for another reason:
+ * - waitUntilDurable() calls update the replication JournalListener, so more frequent calls may be
+ * helpful to unblock replication related operations more quickly.
+ */
class JournalFlusher : public BackgroundJob {
public:
- explicit JournalFlusher() : BackgroundJob(/*deleteSelf*/ false) {}
+ /**
+ * Setting 'disablePeriodicFlushes' to true will cause the JournalFlusher thread to only execute
+ * a data flush upon explicit request: flushes will no longer be executed periodically in
+ * addition. This is useful for storage engines that do not want frequent durability updates,
+ * like engines without a journal where the cost of durability is high (using checkpoints
+ * instead).
+ */
+ explicit JournalFlusher(bool disablePeriodicFlushes)
+ : BackgroundJob(/*deleteSelf*/ false), _disablePeriodicFlushes(disablePeriodicFlushes) {}
static JournalFlusher* get(ServiceContext* serviceCtx);
static JournalFlusher* get(OperationContext* opCtx);
@@ -50,6 +73,11 @@ public:
return "JournalFlusher";
}
+ /**
+ * Runs data flushes every 'storageGlobalParams.journalCommitIntervalMs' millis (unless
+ * '_disablePeriodicFlushes' is set) or immediately if triggerJournalFlush() or
+ * waitForJournalFlush() is called.
+ */
void run();
/**
@@ -101,6 +129,11 @@ private:
std::make_unique<SharedPromise<void>>();
std::unique_ptr<SharedPromise<void>> _nextSharedPromise =
std::make_unique<SharedPromise<void>>();
+
+ // Controls whether to ignore the 'storageGlobalParams.journalCommitIntervalMs' setting. If set,
+ // data flushes will only be executed upon explicit request, no longer periodically in addition
+ // to upon request.
+ bool _disablePeriodicFlushes;
};
} // namespace mongo
diff --git a/src/mongo/db/storage/control/storage_control.cpp b/src/mongo/db/storage/control/storage_control.cpp
index 46534da1db5..6160e0bb0f7 100644
--- a/src/mongo/db/storage/control/storage_control.cpp
+++ b/src/mongo/db/storage/control/storage_control.cpp
@@ -43,50 +43,56 @@ namespace mongo {
namespace StorageControl {
-void startStorageControls(ServiceContext* serviceContext) {
+namespace {
+
+bool areControlsStarted = false;
+
+} // namespace
+
+void startStorageControls(ServiceContext* serviceContext, bool forTestOnly) {
auto storageEngine = serviceContext->getStorageEngine();
// Instantiate a thread to periodically, and upon request, flush writes to disk.
- if (!storageEngine->isEphemeral() && storageEngine->isDurable()) {
- std::unique_ptr<JournalFlusher> journalFlusher = std::make_unique<JournalFlusher>();
- journalFlusher->go();
- JournalFlusher::set(serviceContext, std::move(journalFlusher));
- }
+ //
+ // Persisted storage engines that have a journal should periodically flush the journal to disk
+ // to avoid risking much user data loss across a server crash if the user is not doing {j: true}
+ // writes often.
+ //
+ // Non-durable, i.e. no journal, storage engines should only flush upon request because
+ // waitUntilDurable() will perform a checkpoint and checkpoints are costly. Periodic flushes
+ // will be disabled and only requests will provoke a flush.
+ //
+ // Ephemeral engines are not durable -- waitUntilDurable() returns early -- but frequent updates
+ // to replication's JournalListener in the waitUntilDurable() code may help update replication
+ // timestamps more quickly.
+ //
+ // (Note: the ephemeral engine returns false for isDurable(), so we must be careful not to
+ // disable it.)
+ std::unique_ptr<JournalFlusher> journalFlusher = std::make_unique<JournalFlusher>(
+ /*disablePeriodicFlushes*/ forTestOnly ||
+ (!storageEngine->isDurable() && !storageEngine->isEphemeral()));
+ journalFlusher->go();
+ JournalFlusher::set(serviceContext, std::move(journalFlusher));
+
+ areControlsStarted = true;
}
void stopStorageControls(ServiceContext* serviceContext) {
- auto storageEngine = serviceContext->getStorageEngine();
-
- if (!storageEngine->isEphemeral() && storageEngine->isDurable()) {
+ if (areControlsStarted) {
JournalFlusher::get(serviceContext)->shutdown();
}
}
void triggerJournalFlush(ServiceContext* serviceContext) {
- auto storageEngine = serviceContext->getStorageEngine();
-
- if (!storageEngine->isEphemeral() && storageEngine->isDurable()) {
- JournalFlusher::get(serviceContext)->triggerJournalFlush();
- }
+ JournalFlusher::get(serviceContext)->triggerJournalFlush();
}
void waitForJournalFlush(OperationContext* opCtx) {
- auto serviceContext = opCtx->getServiceContext();
- auto storageEngine = serviceContext->getStorageEngine();
-
- if (!storageEngine->isEphemeral() && storageEngine->isDurable()) {
- JournalFlusher::get(serviceContext)->waitForJournalFlush();
- } else {
- opCtx->recoveryUnit()->waitUntilDurable(opCtx);
- }
+ JournalFlusher::get(opCtx)->waitForJournalFlush();
}
void interruptJournalFlusherForReplStateChange(ServiceContext* serviceContext) {
- auto storageEngine = serviceContext->getStorageEngine();
-
- if (!storageEngine->isEphemeral() && storageEngine->isDurable()) {
- JournalFlusher::get(serviceContext)->interruptJournalFlusherForReplStateChange();
- }
+ JournalFlusher::get(serviceContext)->interruptJournalFlusherForReplStateChange();
}
} // namespace StorageControl
diff --git a/src/mongo/db/storage/control/storage_control.h b/src/mongo/db/storage/control/storage_control.h
index 75a1289adbf..e54cb51469f 100644
--- a/src/mongo/db/storage/control/storage_control.h
+++ b/src/mongo/db/storage/control/storage_control.h
@@ -41,23 +41,24 @@ class ServiceContext;
namespace StorageControl {
/**
- * Responsible for initializing independent processes for replication that and interact with the
- * storage layer.
+ * Responsible for initializing independent processes for replication that interact with the storage
+ * layer.
*
- * Instantiates the JournalFlusher to periodically, and upon request, flush writes to disk.
+ * Instantiates the JournalFlusher to flush writes to disk periodically and upon request. If
+ * 'forTestOnly' is set, then the JournalFlusher will only run upon request so as not to disrupt
+ * unit test expectations.
*
- * Safe to call again after stopStorageControls has been called, to restart any processes that were
- * stopped.
+ * Safe to call again after stopStorageControls() has been called, to restart any processes that
+ * were stopped.
*/
-void startStorageControls(ServiceContext* serviceContext);
+void startStorageControls(ServiceContext* serviceContext, bool forTestOnly = false);
/**
- * Stops the processes begun by startStorageControls.
+ * Stops the processes begun by startStorageControls().
*
- * The OplogCapMaintainerThread is unowned and therefore ignored; the JournalFlusher is shut
- * down.
+ * The JournalFlusher is shut down.
*
- * Safe to call multiple times.
+ * Safe to call multiple times, whether or not startStorageControls() has been called.
*/
void stopStorageControls(ServiceContext* serviceContext);