|
28 | 28 | #include "replicationthrottle.h" |
29 | 29 | #include "rollback_result.h" |
30 | 30 | #include "statwriter.h" |
| 31 | +#include <checkpoint_manager.h> |
31 | 32 |
|
32 | 33 | #include <platform/sized_buffer.h> |
33 | 34 |
|
| 35 | +#include <algorithm> |
| 36 | + |
34 | 37 | /** |
35 | 38 | * A configuration value changed listener that responds to Ephemeral bucket |
36 | 39 | * parameter changes. |
@@ -148,6 +151,70 @@ bool EphemeralBucket::initialize() { |
148 | 151 | return true; |
149 | 152 | } |
150 | 153 |
|
| 154 | +size_t EphemeralBucket::getPageableMemCurrent() const { |
| 155 | + // Ephemeral buckets differ from persistent in terms of how memory can |
| 156 | + // be freed - only active items can (potentially) be directly deleted |
| 157 | + // (auto-delete or items which have expired) - given that the replica |
| 158 | + // must be an exact copy of the active. |
| 159 | + // As such, 'pageable' memory is non-replica memmory. |
| 160 | + |
| 161 | + // We don't directly track "active_mem_used", but we can roughtly estimate |
| 162 | + // it from mem_used - replica_ht_mem - replica_checkpoint_mem |
| 163 | + const auto estimatedActiveMemory = |
| 164 | + int64_t(stats.getEstimatedTotalMemoryUsed()) - |
| 165 | + stats.replicaHTMemory - stats.replicaCheckpointOverhead; |
| 166 | + return std::max(estimatedActiveMemory, int64_t(0)); |
| 167 | +} |
| 168 | + |
| 169 | +size_t EphemeralBucket::getPageableMemHighWatermark() const { |
| 170 | + // Ephemeral buckets can only page out non-replica memory (see comments |
| 171 | + // in getPageableMemCurrent). As such, set pagable high watermark to |
| 172 | + // a fraction of the overall high watermark based on what faction of |
| 173 | + // vBuckets are active. Memory used by any dead vbs should be reclaimed |
| 174 | + // soon, so ignore them here; they don't need to be allocated a portion |
| 175 | + // of the quota. |
| 176 | + const double activeVBCount = vbMap.getVBStateCount(vbucket_state_active); |
| 177 | + const double pendingVBCount = vbMap.getVBStateCount(vbucket_state_pending); |
| 178 | + const double replicaVBCount = vbMap.getVBStateCount(vbucket_state_replica); |
| 179 | + const double totalVBCount = activeVBCount + pendingVBCount + replicaVBCount; |
| 180 | + |
| 181 | + if (totalVBCount <= 0) { |
| 182 | + // not an expected situation, bail out and return the full high |
| 183 | + // watermark |
| 184 | + return stats.mem_high_wat.load(); |
| 185 | + } |
| 186 | + |
| 187 | + const double activePendingHighWat = |
| 188 | + (stats.mem_high_wat.load() / totalVBCount) * |
| 189 | + (activeVBCount + pendingVBCount); |
| 190 | + |
| 191 | + return activePendingHighWat; |
| 192 | +} |
| 193 | + |
| 194 | +size_t EphemeralBucket::getPageableMemLowWatermark() const { |
| 195 | + // Ephemeral buckets can only page out non-replica memory (see comments |
| 196 | + // in getPageableMemCurrent). As such, set pagable low watermark to |
| 197 | + // a fraction of the overall low watermark based on what faction of |
| 198 | + // vBuckets are active. Memory used by any dead vbs should be reclaimed |
| 199 | + // soon, so ignore them here; they don't need to be allocated a portion |
| 200 | + // of the quota. |
| 201 | + const double activeVBCount = vbMap.getVBStateCount(vbucket_state_active); |
| 202 | + const double pendingVBCount = vbMap.getVBStateCount(vbucket_state_pending); |
| 203 | + const double replicaVBCount = vbMap.getVBStateCount(vbucket_state_replica); |
| 204 | + const double totalVBCount = activeVBCount + pendingVBCount + replicaVBCount; |
| 205 | + |
| 206 | + if (totalVBCount <= 0) { |
| 207 | + // not an expected situation, bail out and return the full low |
| 208 | + // watermark |
| 209 | + return stats.mem_low_wat.load(); |
| 210 | + } |
| 211 | + |
| 212 | + const double activeLowWat = (stats.mem_low_wat.load() / totalVBCount) * |
| 213 | + (activeVBCount + pendingVBCount); |
| 214 | + |
| 215 | + return activeLowWat; |
| 216 | +} |
| 217 | + |
151 | 218 | void EphemeralBucket::attemptToFreeMemory() { |
152 | 219 | // Call down to the base class; do to whatever it can to free memory. |
153 | 220 | KVBucket::attemptToFreeMemory(); |
@@ -200,28 +267,40 @@ VBucketPtr EphemeralBucket::makeVBucket( |
200 | 267 | // 1. make_shared doesn't accept a Deleter |
201 | 268 | // 2. allocate_shared has inconsistencies between platforms in calling |
202 | 269 | // alloc.destroy (libc++ doesn't call it) |
203 | | - return VBucketPtr(new EphemeralVBucket(id, |
204 | | - state, |
205 | | - stats, |
206 | | - engine.getCheckpointConfig(), |
207 | | - shard, |
208 | | - lastSeqno, |
209 | | - lastSnapStart, |
210 | | - lastSnapEnd, |
211 | | - std::move(table), |
212 | | - std::move(newSeqnoCb), |
213 | | - makeSyncWriteResolvedCB(), |
214 | | - makeSyncWriteCompleteCB(), |
215 | | - makeSeqnoAckCB(), |
216 | | - engine.getConfiguration(), |
217 | | - eviction_policy, |
218 | | - std::move(manifest), |
219 | | - initState, |
220 | | - purgeSeqno, |
221 | | - maxCas, |
222 | | - mightContainXattrs, |
223 | | - replicationTopology), |
224 | | - VBucket::DeferredDeleter(engine)); |
| 270 | + auto* vb = new EphemeralVBucket(id, |
| 271 | + state, |
| 272 | + stats, |
| 273 | + engine.getCheckpointConfig(), |
| 274 | + shard, |
| 275 | + lastSeqno, |
| 276 | + lastSnapStart, |
| 277 | + lastSnapEnd, |
| 278 | + std::move(table), |
| 279 | + std::move(newSeqnoCb), |
| 280 | + makeSyncWriteResolvedCB(), |
| 281 | + makeSyncWriteCompleteCB(), |
| 282 | + makeSeqnoAckCB(), |
| 283 | + engine.getConfiguration(), |
| 284 | + eviction_policy, |
| 285 | + std::move(manifest), |
| 286 | + initState, |
| 287 | + purgeSeqno, |
| 288 | + maxCas, |
| 289 | + mightContainXattrs, |
| 290 | + replicationTopology); |
| 291 | + |
| 292 | + vb->ht.setMemChangedCallback([this, vb](int64_t delta) { |
| 293 | + if (vb->getState() == vbucket_state_replica) { |
| 294 | + this->stats.replicaHTMemory += delta; |
| 295 | + } |
| 296 | + }); |
| 297 | + vb->checkpointManager->setOverheadChangedCallback( |
| 298 | + [this, vb](int64_t delta) { |
| 299 | + if (vb->getState() == vbucket_state_replica) { |
| 300 | + this->stats.replicaCheckpointOverhead += delta; |
| 301 | + } |
| 302 | + }); |
| 303 | + return VBucketPtr(vb, VBucket::DeferredDeleter(engine)); |
225 | 304 | } |
226 | 305 |
|
227 | 306 | void EphemeralBucket::completeStatsVKey(const void* cookie, |
|
0 commit comments