Skip to content

Commit

Permalink
SERVER-86954 Perform ticket acquisition for all priority levels (#19175)
Browse files Browse the repository at this point in the history
GitOrigin-RevId: 27374bce7ec360d1510bacbda292a2d609792860
  • Loading branch information
mbroadst authored and MongoDB Bot committed Feb 23, 2024
1 parent bd965e3 commit 0e12f63
Show file tree
Hide file tree
Showing 9 changed files with 26 additions and 64 deletions.
14 changes: 7 additions & 7 deletions src/mongo/db/catalog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1671,22 +1671,22 @@ _Code spelunking starting points:_
# Global Lock Admission Control
There are 2 separate ticketing mechanisms placed in front of the global lock acquisition. Both aim to limit the number of concurrent operations from overwhelming the system. Before an operation can acquire the global lock, it must acquire a ticket from one, or both, of the ticketing mechanisms. When both ticket mechanisms are necessary, the acquisition order is as follows:
1. Flow Control - Required only for global lock requests in MODE_IX
2. Execution Admission Control - Required for all global lock requests
2. Execution Control - Required for all global lock requests


Flow Control is in place to prevent a majority of secondaries from falling behind in replication, whereas Execution Admission Control aims to limit the number of concurrent storage engine transactions on a single node.
Flow Control is in place to prevent a majority of secondaries from falling behind in replication, whereas Execution Control aims to limit the number of concurrent storage engine transactions on a single node.

## Admission Priority
Associated with every operation is an admission priority, stored as a part of the [AdmissionContext](https://github.com/mongodb/mongo/blob/r6.3.0-rc0/src/mongo/util/concurrency/admission_context.h#L40). By default, operations are 'normal' priority.

In both the Execution Admission and Flow Control ticketing system, operations of 'immediate' priority bypass ticket acquisition, regardless of ticket availability. Otherwise, tickets that are not 'immediate' priority must throttle when there are no tickets available.
In the Flow Control ticketing system, operations of 'immediate' priority bypass ticket acquisition regardless of ticket availability. Tickets that are not 'immediate' priority must throttle when there are no tickets available in both Flow Control and Execution Control.

Flow Control is only concerned whether an operation is 'immediate' priority and does not differentiate between 'normal' and 'low' priorities. The current version of Execution Admission Control relies on admission priority to administer tickets when the server is under load.
Flow Control is only concerned whether an operation is 'immediate' priority and does not differentiate between 'normal' and 'low' priorities. The current version of Execution Control relies on admission priority to administer tickets when the server is under load.

**AdmissionContext::Priority**
* `kImmediate` - An operation that bypasses ticket acquisition in both ticketing mechanisms. Reserved for operations critical to availability (e.g replication workers), or observability (e.g. FTDC), and any operation releasing resources (e.g. committing or aborting prepared transactions).
* `kImmediate` - Reserved for operations critical to availability (e.g replication workers), or observability (e.g. FTDC), and any operation releasing resources (e.g. committing or aborting prepared transactions).
* `kNormal` - An operation that should be throttled when the server is under load. If an operation is throttled, it will not affect availability or observability. Most operations, both user and internal, should use this priority unless they qualify as 'kLow' or 'kImmediate' priority.
* `kLow` - It's of low importance that the operation acquires a ticket in Execution Admission Control. Reserved for background tasks that have no other operations dependent on them. The operation will be throttled under load and make significantly less progress compared to operations of higher priorities in the Execution Admission Control.
* `kLow` - Reserved for background tasks that have no other operations dependent on them. The operation will be throttled under load and make significantly less progress compared to operations of higher priorities in the Execution Control.

[See AdmissionContext::Priority for more details](https://github.com/mongodb/mongo/blob/r7.0.0-rc0/src/mongo/util/concurrency/admission_context.h#L45-L67).

Expand Down Expand Up @@ -1717,7 +1717,7 @@ Examples of Deprioritized Operations:
* [Unbounded Collection Scans](https://github.com/mongodb/mongo/blob/0ef2c68f58ea20c2dde99e5ce3ea10b79e18453d/src/mongo/db/query/planner_analysis.cpp#L1254)
* Index Builds [(1)](https://github.com/mongodb/mongo/blob/0ef2c68f58ea20c2dde99e5ce3ea10b79e18453d/src/mongo/db/index_builds_coordinator.cpp#L3064), [(2)](https://github.com/mongodb/mongo/blob/0ef2c68f58ea20c2dde99e5ce3ea10b79e18453d/src/mongo/db/index_builds_coordinator.cpp#L3105)

## Execution Admission Control
## Execution Control
A ticketing mechanism that limits the number of concurrent storage engine transactions in a single mongod to reduce contention on storage engine resources.

### Ticket Management
Expand Down
4 changes: 1 addition & 3 deletions src/mongo/db/concurrency/locker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -986,9 +986,7 @@ bool Locker::_acquireTicket(OperationContext* opCtx, LockMode mode, Date_t deadl
auto holder = _ticketHolderManager ? _ticketHolderManager->getTicketHolder(mode) : nullptr;
const bool reader = isSharedLockMode(mode);

if (!shouldWaitForTicket() && holder) {
holder->reportImmediatePriorityAdmission();
} else if (mode != MODE_X && mode != MODE_NONE && holder) {
if (mode != MODE_X && mode != MODE_NONE && holder) {
// MODE_X is exclusive of all other locks, thus acquiring a ticket is unnecessary.
_clientState.store(reader ? kQueuedReader : kQueuedWriter);
// If the ticket wait is interrupted, restore the state of the client.
Expand Down
1 change: 1 addition & 0 deletions src/mongo/db/d_concurrency_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#include "mongo/logv2/log_attr.h"
#include "mongo/logv2/log_component.h"
#include "mongo/platform/atomic_word.h"
#include "mongo/stdx/future.h"
#include "mongo/stdx/thread.h"
#include "mongo/unittest/assert.h"
#include "mongo/unittest/framework.h"
Expand Down
3 changes: 0 additions & 3 deletions src/mongo/util/concurrency/priority_ticketholder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#include <algorithm>
#include <boost/move/utility_core.hpp>
#include <boost/none.hpp>

#include <boost/optional/optional.hpp>

#include "mongo/db/service_context.h"
Expand Down Expand Up @@ -122,8 +121,6 @@ boost::optional<Ticket> PriorityTicketHolder::_waitForTicketUntilImpl(Interrupti
}

void PriorityTicketHolder::_releaseToTicketPoolImpl(AdmissionContext* admCtx) noexcept {
// 'Immediate' priority operations should bypass the ticketing system completely.
invariant(admCtx && admCtx->getPriority() != AdmissionContext::Priority::kImmediate);
_pool.release();
}

Expand Down
4 changes: 0 additions & 4 deletions src/mongo/util/concurrency/priority_ticketholder.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,15 @@
#include <array>
#include <boost/optional/optional.hpp>
#include <cstdint>
#include <queue>
#include <type_traits>

#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/service_context.h"
#include "mongo/platform/mutex.h"
#include "mongo/stdx/condition_variable.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/concurrency/admission_context.h"
#include "mongo/util/concurrency/mutex.h"
#include "mongo/util/concurrency/ticket_pool.h"
#include "mongo/util/concurrency/ticketholder.h"
#include "mongo/util/hierarchical_acquisition.h"
#include "mongo/util/time_support.h"

namespace mongo {
Expand Down
7 changes: 3 additions & 4 deletions src/mongo/util/concurrency/semaphore_ticketholder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@

#include "mongo/util/concurrency/semaphore_ticketholder.h"

#include <boost/move/utility_core.hpp>
#include <boost/none.hpp>
#include <cerrno>
#include <ctime>
#include <string>

#include <boost/move/utility_core.hpp>
#include <boost/none.hpp>
#include <boost/optional/optional.hpp>

#include "mongo/db/service_context.h"
Expand Down Expand Up @@ -175,12 +174,12 @@ boost::optional<Ticket> SemaphoreTicketHolder::_waitForTicketUntilImpl(Interrupt
AdmissionContext* admCtx,
Date_t until) {
stdx::unique_lock<Latch> lk(_mutex);

bool taken = interruptible.waitForConditionOrInterruptUntil(
_newTicket, lk, until, [this] { return _tryAcquire(); });
if (!taken) {
return boost::none;
}

return Ticket{this, admCtx};
}

Expand Down
6 changes: 1 addition & 5 deletions src/mongo/util/concurrency/semaphore_ticketholder.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,14 @@
#include <algorithm>
#include <boost/optional/optional.hpp>
#include <cstdint>
#include <queue>

#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/service_context.h"
#include "mongo/platform/atomic_word.h"
#include "mongo/platform/mutex.h"
#include "mongo/stdx/condition_variable.h"
#include "mongo/util/concurrency/admission_context.h"
#include "mongo/util/concurrency/mutex.h"
#include "mongo/util/concurrency/ticketholder.h"
#include "mongo/util/hierarchical_acquisition.h"
#include "mongo/util/time_support.h"

namespace mongo {
Expand Down Expand Up @@ -83,7 +79,6 @@ class SemaphoreTicketHolder final : public TicketHolder {
}
#if defined(__linux__)
mutable sem_t _sem;

#else
bool _tryAcquire();

Expand All @@ -92,6 +87,7 @@ class SemaphoreTicketHolder final : public TicketHolder {
MONGO_MAKE_LATCH(HierarchicalAcquisitionLevel(0), "SemaphoreTicketHolder::_mutex");
stdx::condition_variable _newTicket;
#endif

QueueStats _semaphoreStats;
};

Expand Down
24 changes: 12 additions & 12 deletions src/mongo/util/concurrency/ticketholder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,10 @@

#include <algorithm>
#include <boost/none.hpp>
#include <mutex>

#include <boost/move/utility_core.hpp>
#include <boost/optional/optional.hpp>

#include "mongo/db/feature_flag.h"
#include "mongo/db/storage/execution_control/concurrency_adjustment_parameters_gen.h"
#include "mongo/db/storage/storage_engine_feature_flags_gen.h"
#include "mongo/idl/idl_parser.h"
#include "mongo/logv2/log.h"
#include "mongo/util/duration.h"
#include "mongo/util/fail_point.h"
Expand Down Expand Up @@ -111,17 +106,22 @@ void TicketHolder::appendStats(BSONObjBuilder& b) const {
b.append("out", used());
b.append("available", available());
b.append("totalTickets", outof());
b.append("immediatePriorityAdmissionsCount", getImmediatePriorityAdmissionsCount());
b.append("immediatePriorityAdmissionsCount", _immediatePriorityAdmissionsCount.loadRelaxed());
_appendImplStats(b);
}

void TicketHolder::_releaseToTicketPool(AdmissionContext* admCtx) noexcept {
if (admCtx->getPriority() == AdmissionContext::Priority::kImmediate) {
return;
}

if (MONGO_unlikely(hangTicketRelease.shouldFail())) {
LOGV2(8435300,
"Hanging hangTicketRelease in _releaseToTicketPool() due to 'hangTicketRelease' "
"failpoint");
hangTicketRelease.pauseWhileSet();
}

auto& queueStats = _getQueueStatsToUse(admCtx);
updateQueueStatsOnRelease(_serviceContext, queueStats, admCtx);
_releaseToTicketPoolImpl(admCtx);
Expand All @@ -136,25 +136,25 @@ Ticket TicketHolder::waitForTicket(Interruptible& interruptible,
}

boost::optional<Ticket> TicketHolder::tryAcquire(AdmissionContext* admCtx) {
// 'kImmediate' operations should always bypass the ticketing system.
invariant(admCtx && admCtx->getPriority() != AdmissionContext::Priority::kImmediate);
auto ticket = _tryAcquireImpl(admCtx);
if (admCtx->getPriority() == AdmissionContext::Priority::kImmediate) {
_immediatePriorityAdmissionsCount.fetchAndAdd(1);
return Ticket{this, admCtx};
}

auto ticket = _tryAcquireImpl(admCtx);
if (ticket) {
auto& queueStats = _getQueueStatsToUse(admCtx);
updateQueueStatsOnTicketAcquisition(_serviceContext, queueStats, admCtx);
_updatePeakUsed();
}

return ticket;
}


boost::optional<Ticket> TicketHolder::waitForTicketUntil(Interruptible& interruptible,
AdmissionContext* admCtx,
Date_t until,
Microseconds& timeQueuedForTicketMicros) {
invariant(admCtx && admCtx->getPriority() != AdmissionContext::Priority::kImmediate);

// Attempt a quick acquisition first.
if (auto ticket = tryAcquire(admCtx)) {
return ticket;
Expand Down
27 changes: 1 addition & 26 deletions src/mongo/util/concurrency/ticketholder.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,13 @@
#include <boost/move/utility_core.hpp>
#include <boost/optional/optional.hpp>
#include <cstdint>
#include <utility>

#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/service_context.h"
#include "mongo/platform/atomic_word.h"
#include "mongo/platform/mutex.h"
#include "mongo/stdx/condition_variable.h"
#include "mongo/stdx/future.h"
#include "mongo/util/assert_util_core.h"
#include "mongo/util/concurrency/admission_context.h"
#include "mongo/util/concurrency/mutex.h"
#include "mongo/util/hierarchical_acquisition.h"
#include "mongo/util/time_support.h"

namespace mongo {
Expand Down Expand Up @@ -118,24 +113,6 @@ class TicketHolder {
*/
int32_t getAndResetPeakUsed();

/**
* 'Immediate' admissions don't need to acquire or wait for a ticket. However, they should
* report to the TicketHolder for tracking purposes.
*
* Increments the count of 'immediate' priority admissions reported.
*/
virtual void reportImmediatePriorityAdmission() {
_immediatePriorityAdmissionsCount.fetchAndAdd(1);
}

/**
* Returns the number of 'immediate' priority admissions, which always bypass ticket
* acquisition.
*/
int64_t getImmediatePriorityAdmissionsCount() const {
return _immediatePriorityAdmissionsCount.loadRelaxed();
}

virtual void appendStats(BSONObjBuilder& b) const;

/**
Expand Down Expand Up @@ -202,7 +179,7 @@ class TicketHolder {

Mutex _resizeMutex =
MONGO_MAKE_LATCH(HierarchicalAcquisitionLevel(2), "TicketHolder::_resizeMutex");
AtomicWord<std::int64_t> _immediatePriorityAdmissionsCount;
AtomicWord<std::int64_t> _immediatePriorityAdmissionsCount{0};

protected:
/**
Expand Down Expand Up @@ -233,8 +210,6 @@ class MockTicketHolder : public TicketHolder {

void appendStats(BSONObjBuilder& b) const override {}

void reportImmediatePriorityAdmission() override {}

int32_t available() const override {
return _available;
}
Expand Down

0 comments on commit 0e12f63

Please sign in to comment.