summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/sbe_plan_cache.cpp
blob: 07d904b150600cc2bcd66e23c26eb5109754efd4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/**
 *    Copyright (C) 2021-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    <http://www.mongodb.com/licensing/server-side-public-license>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 */

#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery

#include "mongo/db/query/sbe_plan_cache.h"

#include "mongo/db/query/plan_cache_invalidator.h"
#include "mongo/db/query/plan_cache_size_parameter.h"
#include "mongo/db/server_options.h"
#include "mongo/logv2/log.h"
#include "mongo/util/processinfo.h"

namespace mongo::sbe {
namespace {

const auto sbePlanCacheDecoration =
    ServiceContext::declareDecoration<std::unique_ptr<sbe::PlanCache>>();

class SbePlanCacheInvalidatorCallback final : public PlanCacheInvalidatorCallback {
public:
    SbePlanCacheInvalidatorCallback(ServiceContext* serviceCtx) : _serviceCtx{serviceCtx} {}

    void invalidateCacheEntriesWith(UUID collectionUuid, size_t oldVersion) override {
        clearPlanCache(_serviceCtx, collectionUuid, oldVersion);
    }

private:
    ServiceContext* _serviceCtx;
};

size_t convertToSizeInBytes(const plan_cache_util::PlanCacheSizeParameter& param) {
    constexpr size_t kBytesInMB = 1014 * 1024;
    constexpr size_t kMBytesInGB = 1014;

    double sizeInMB = param.size;

    switch (param.units) {
        case plan_cache_util::PlanCacheSizeUnits::kPercent:
            sizeInMB *= ProcessInfo::getMemSizeMB() / 100.0;
            break;
        case plan_cache_util::PlanCacheSizeUnits::kMB:
            break;
        case plan_cache_util::PlanCacheSizeUnits::kGB:
            sizeInMB *= kMBytesInGB;
            break;
    }

    return static_cast<size_t>(sizeInMB * kBytesInMB);
}

/**
 * Sets upper size limit on the PlanCache size to 500GB or 25% of the system's memory, whichever is
 * smaller.
 */
size_t capPlanCacheSize(size_t planCacheSize) {
    constexpr size_t kBytesInGB = 1024 * 1024 * 1024;

    // Maximum size of the plan cache expressed in bytes.
    constexpr size_t kMaximumPlanCacheSize = 500 * kBytesInGB;

    // Maximum size of the plan cache expressed as a share of the memory available to the process.
    const plan_cache_util::PlanCacheSizeParameter limitToProcessSize{
        25, plan_cache_util::PlanCacheSizeUnits::kPercent};
    const size_t limitToProcessSizeInBytes = convertToSizeInBytes(limitToProcessSize);

    // The size will be capped by the minimum of the two values defined above.
    const size_t maxPlanCacheSize = std::min(kMaximumPlanCacheSize, limitToProcessSizeInBytes);

    if (planCacheSize > maxPlanCacheSize) {
        planCacheSize = maxPlanCacheSize;
        LOGV2_DEBUG(6007000,
                    1,
                    "The plan cache size has been capped",
                    "maxPlanCacheSize"_attr = maxPlanCacheSize);
    }

    return planCacheSize;
}

size_t getPlanCacheSizeInBytes(const plan_cache_util::PlanCacheSizeParameter& param) {
    size_t planCacheSize = convertToSizeInBytes(param);
    uassert(5968001,
            "Cache size must be at least 1KB * number of cores",
            planCacheSize >= 1024 * ProcessInfo::getNumCores());
    return capPlanCacheSize(planCacheSize);
}

class PlanCacheSizeUpdaterImpl final : public plan_cache_util::PlanCacheSizeUpdater {
public:
    void update(ServiceContext* serviceCtx,
                plan_cache_util::PlanCacheSizeParameter parameter) final {
        if (feature_flags::gFeatureFlagSbePlanCache.isEnabledAndIgnoreFCV()) {
            auto size = getPlanCacheSizeInBytes(parameter);
            auto& globalPlanCache = sbePlanCacheDecoration(serviceCtx);
            globalPlanCache->reset(size);
        }
    }
};

ServiceContext::ConstructorActionRegisterer planCacheRegisterer{
    "PlanCacheRegisterer", [](ServiceContext* serviceCtx) {
        plan_cache_util::sbePlanCacheSizeUpdaterDecoration(serviceCtx) =
            std::make_unique<PlanCacheSizeUpdaterImpl>();

        PlanCacheInvalidatorCallback::set(
            serviceCtx, std::make_unique<SbePlanCacheInvalidatorCallback>(serviceCtx));
        if (feature_flags::gFeatureFlagSbePlanCache.isEnabledAndIgnoreFCV()) {
            auto status = plan_cache_util::PlanCacheSizeParameter::parse(planCacheSize.get());
            uassertStatusOK(status);

            auto size = getPlanCacheSizeInBytes(status.getValue());
            auto& globalPlanCache = sbePlanCacheDecoration(serviceCtx);
            globalPlanCache = std::make_unique<sbe::PlanCache>(size, ProcessInfo::getNumCores());
        }
    }};

}  // namespace

sbe::PlanCache& getPlanCache(ServiceContext* serviceCtx) {
    uassert(5933402,
            "Cannot getPlanCache() if gFeatureFlagSbePlanCache is disabled",
            feature_flags::gFeatureFlagSbePlanCache.isEnabledAndIgnoreFCV());
    return *sbePlanCacheDecoration(serviceCtx);
}

sbe::PlanCache& getPlanCache(OperationContext* opCtx) {
    uassert(5933401,
            "Cannot getPlanCache() if gFeatureFlagSbePlanCache is disabled",
            feature_flags::gFeatureFlagSbePlanCache.isEnabledAndIgnoreFCV());
    tassert(5933400, "Cannot get the global SBE plan cache by a nullptr", opCtx);
    return getPlanCache(opCtx->getServiceContext());
}

void clearPlanCache(ServiceContext* serviceCtx, UUID collectionUuid, size_t collectionVersion) {
    if (feature_flags::gFeatureFlagSbePlanCache.isEnabledAndIgnoreFCV()) {
        auto removed = sbe::getPlanCache(serviceCtx)
                           .removeIf([&collectionUuid, collectionVersion](const PlanCacheKey& key) {
                               return key.getCollectionVersion() == collectionVersion &&
                                   key.getCollectionUuid() == collectionUuid;
                           });

        LOGV2_DEBUG(6006600,
                    1,
                    "Clearing SBE Plan Cache",
                    "collectionUuid"_attr = collectionUuid,
                    "collectionVersion"_attr = collectionVersion,
                    "removedEntries"_attr = removed);
    }
}
}  // namespace mongo::sbe