YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/util/mem_tracker.h
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
#ifndef YB_UTIL_MEM_TRACKER_H
33
#define YB_UTIL_MEM_TRACKER_H
34
35
#include <stdint.h>
36
37
#include <functional>
38
#include <memory>
39
#include <string>
40
#include <vector>
41
#include <unordered_map>
42
43
#ifdef TCMALLOC_ENABLED
44
#include <gperftools/malloc_extension.h>
45
#endif
46
47
#include <boost/optional.hpp>
48
49
#include "yb/gutil/ref_counted.h"
50
#include "yb/util/high_water_mark.h"
51
#include "yb/util/locks.h"
52
#include "yb/util/monotime.h"
53
#include "yb/util/mutex.h"
54
#include "yb/util/random.h"
55
#include "yb/util/strongly_typed_bool.h"
56
57
namespace yb {
58
59
class Status;
60
class MemTracker;
61
class MetricEntity;
62
typedef std::shared_ptr<MemTracker> MemTrackerPtr;
63
64
// Garbage collector is used by MemTracker to free memory allocated by caches when reached
65
// soft memory limit.
66
class GarbageCollector {
67
 public:
68
  virtual void CollectGarbage(size_t required) = 0;
69
70
 protected:
71
496
  ~GarbageCollector() {}
72
};
73
74
YB_STRONGLY_TYPED_BOOL(MayExist);
75
YB_STRONGLY_TYPED_BOOL(AddToParent);
76
YB_STRONGLY_TYPED_BOOL(CreateMetrics);
77
YB_STRONGLY_TYPED_BOOL(OnlyChildren);
78
79
typedef std::function<int64_t()> ConsumptionFunctor;
80
typedef std::function<void()> PollChildrenConsumptionFunctors;
81
82
struct SoftLimitExceededResult {
83
  bool exceeded;
84
  double current_capacity_pct;
85
};
86
87
// A MemTracker tracks memory consumption; it contains an optional limit and is
88
// arranged into a tree structure such that the consumption tracked by a
89
// MemTracker is also tracked by its ancestors.
90
//
91
// The MemTracker hierarchy is rooted in a single static MemTracker whose limi
92
// is set via gflag. The root MemTracker always exists, and it is the common
93
// ancestor to all MemTrackers. All operations that discover MemTrackers begin
94
// at the root and work their way down the tree, while operations that deal
95
// with adjusting memory consumption begin at a particular MemTracker and work
96
// their way up the tree to the root. The tree structure is strictly enforced:
97
// all MemTrackers (except the root) must have a parent, and all children
98
// belonging to a parent must have unique ids.
99
//
100
// When a MemTracker begins its life, it has a strong reference to its parent
101
// and the parent has a weak reference to it. The strong reference remains for
102
// the lifetime of the MemTracker, but the weak reference can be dropped via
103
// UnregisterFromParent(). A MemTracker in this state may continue servicing
104
// memory consumption operations while allowing a new MemTracker with the same
105
// id to be created on the old parent.
106
//
107
// By default, memory consumption is tracked via calls to Consume()/Release(), either to
108
// the tracker itself or to one of its descendents. Alternatively, a consumption function
109
// can be specified, and then the function's value is used as the consumption rather than the
110
// tally maintained by Consume() and Release(). A tcmalloc function is used to track process
111
// memory consumption, since the process memory usage may be higher than the computed
112
// total memory (tcmalloc does not release deallocated memory immediately).
113
//
114
// GcFunctions can be attached to a MemTracker in order to free up memory if the limit is
115
// reached. If LimitExceeded() is called and the limit is exceeded, it will first call the
116
// GcFunctions to try to free memory and recheck the limit. For example, the process
117
// tracker has a GcFunction that releases any unused memory still held by tcmalloc, so
118
// this will be called before the process limit is reported as exceeded. GcFunctions are
119
// called in the order they are added, so expensive functions should be added last.
120
//
121
// This class is thread-safe.
122
//
123
// NOTE: this class has been partially ported over from Impala with
124
// several changes, and as a result the style differs somewhat from
125
// the YB style.
126
//
127
// Changes from Impala:
128
// 1) Id a string vs. a TUniqueId
129
// 2) There is no concept of query trackers vs. pool trackers -- trackers are instead
130
//    associated with objects. Parent hierarchy is preserved, with the assumption that,
131
//    e.g., a tablet server's memtracker will have as its children the tablets' memtrackers,
132
//    which in turn will have memtrackers for their caches, logs, and so forth.
133
//
134
// TODO: this classes uses a lot of statics fields and methods, which
135
// isn't common in YB. It is probably wise to later move the
136
// 'registry' of trackers to a separate class, but it's better to
137
// start using the 'class' *first* and then change this functionality,
138
// depending on how MemTracker ends up being used in YB.
139
class MemTracker : public std::enable_shared_from_this<MemTracker> {
140
 public:
141
  // byte_limit < 0 means no limit
142
  // 'id' is the label for LogUsage() and web UI.
143
  //
144
  // add_to_parent could be set to false in cases when we want to track memory usage of
145
  // some subsystem, but don't want this subsystem to take effect on parent mem tracker.
146
  MemTracker(int64_t byte_limit, const std::string& id,
147
             ConsumptionFunctor consumption_functor,
148
             std::shared_ptr<MemTracker> parent,
149
             AddToParent add_to_parent, CreateMetrics create_metrics);
150
151
  ~MemTracker();
152
153
  #ifdef TCMALLOC_ENABLED
154
  static int64_t GetTCMallocProperty(const char* prop) {
155
    size_t value;
156
    if (!MallocExtension::instance()->GetNumericProperty(prop, &value)) {
157
      LOG(DFATAL) << "Failed to get tcmalloc property " << prop;
158
      value = 0;
159
    }
160
    return value;
161
  }
162
163
  static int64_t GetTCMallocCurrentAllocatedBytes() {
164
    return GetTCMallocProperty("generic.current_allocated_bytes");
165
  }
166
167
  static int64_t GetTCMallocCurrentHeapSizeBytes() {
168
    return GetTCMallocProperty("generic.heap_size");
169
  }
170
171
  static int64_t GetTCMallocActualHeapSizeBytes() {
172
    return GetTCMallocCurrentHeapSizeBytes() -
173
           GetTCMallocProperty("tcmalloc.pageheap_unmapped_bytes");
174
  }
175
  #endif
176
177
  static void SetTCMallocCacheMemory();
178
179
  // Removes this tracker from its parent's children. This tracker retains its
180
  // link to its parent. Must be called on a tracker with a parent.
181
  //
182
  // Automatically called in the MemTracker destructor, but should be called
183
  // explicitly when an object is destroyed if that object is also the "primary
184
  // owner" of a tracker (i.e. the object that originally created the tracker).
185
  // This orphans the tracker so that if the object is recreated, its new
186
  // tracker won't collide with the now orphaned tracker.
187
  //
188
  // Is thread-safe on the parent but not the child. Meaning, multiple trackers
189
  // that share the same parent can all UnregisterFromParent() at the same
190
  // time, but all UnregisterFromParent() calls on a given tracker must be
191
  // externally synchronized.
192
  void UnregisterFromParent();
193
194
  void UnregisterChild(const std::string& id);
195
196
  // Creates and adds the tracker to the tree so that it can be retrieved with
197
  // FindTracker/FindOrCreateTracker.
198
  //
199
  // byte_limit < 0 means no limit; 'id' is a used as a label for LogUsage()
200
  // and web UI and must be unique for the given parent. Use the two-argument
201
  // form if there is no parent.
202
  static std::shared_ptr<MemTracker> CreateTracker(
203
      int64_t byte_limit,
204
      const std::string& id,
205
      const std::shared_ptr<MemTracker>& parent = std::shared_ptr<MemTracker>(),
206
      AddToParent add_to_parent = AddToParent::kTrue,
207
495k
      CreateMetrics create_metrics = CreateMetrics::kTrue) {
208
495k
    return CreateTracker(
209
495k
        byte_limit, id, ConsumptionFunctor(), parent, add_to_parent, create_metrics);
210
495k
  }
211
212
  static std::shared_ptr<MemTracker> CreateTracker(
213
      const std::string& id,
214
      const std::shared_ptr<MemTracker>& parent = std::shared_ptr<MemTracker>(),
215
      AddToParent add_to_parent = AddToParent::kTrue,
216
189k
      CreateMetrics create_metrics = CreateMetrics::kTrue) {
217
189k
    return CreateTracker(-1 /* byte_limit */, id, parent, add_to_parent, create_metrics);
218
189k
  }
219
220
  static std::shared_ptr<MemTracker> CreateTracker(
221
      int64_t byte_limit,
222
      const std::string& id,
223
      ConsumptionFunctor consumption_functor,
224
      const std::shared_ptr<MemTracker>& parent = std::shared_ptr<MemTracker>(),
225
      AddToParent add_to_parent = AddToParent::kTrue,
226
      CreateMetrics create_metrics = CreateMetrics::kTrue);
227
228
  // If a tracker with the specified 'id' and 'parent' exists in the tree, sets
229
  // 'tracker' to reference that instance. Use the two-argument form if there
230
  // is no parent. Returns false if no such tracker exists.
231
  static MemTrackerPtr FindTracker(
232
      const std::string& id,
233
      const MemTrackerPtr& parent = MemTrackerPtr());
234
235
  MemTrackerPtr FindChild(const std::string& id);
236
237
  // If a tracker with the specified 'id' and 'parent' exists in the tree,
238
  // returns a shared_ptr to that instance. Otherwise, creates a new
239
  // MemTracker with the specified byte_limit, id, and parent. Use the two
240
  // argument form if there is no parent.
241
  static std::shared_ptr<MemTracker> FindOrCreateTracker(
242
      int64_t byte_limit,
243
      const std::string& id,
244
      const std::shared_ptr<MemTracker>& parent = std::shared_ptr<MemTracker>(),
245
      AddToParent add_to_parent = AddToParent::kTrue,
246
      CreateMetrics create_metrics = CreateMetrics::kTrue);
247
248
  static std::shared_ptr<MemTracker> FindOrCreateTracker(
249
      const std::string& id,
250
      const std::shared_ptr<MemTracker>& parent = std::shared_ptr<MemTracker>(),
251
      AddToParent add_to_parent = AddToParent::kTrue,
252
16.8M
      CreateMetrics create_metrics = CreateMetrics::kTrue) {
253
16.8M
    return FindOrCreateTracker(-1 /* byte_limit */, id, parent, add_to_parent, create_metrics);
254
16.8M
  }
255
256
  void ListDescendantTrackers(
257
      std::vector<MemTrackerPtr>* trackers, OnlyChildren only_children = OnlyChildren::kFalse);
258
259
  // Returns a list of all children of this tracker.
260
  std::vector<MemTrackerPtr> ListChildren();
261
262
  // Returns a list of all the valid trackers.
263
  static std::vector<MemTrackerPtr> ListTrackers();
264
265
  // Gets a shared_ptr to the "root" tracker, creating it if necessary.
266
  static MemTrackerPtr GetRootTracker();
267
268
  // Tries to update consumption from external source.
269
  // Returns true if consumption was updated, false otherwise.
270
  //
271
  // Currently it uses totally allocated bytes by tcmalloc for root mem tracker when available.
272
  bool UpdateConsumption(bool force = false);
273
274
  // Increases consumption of this tracker and its ancestors by 'bytes'.
275
  void Consume(int64_t bytes);
276
277
  // Try to expand the limit (by asking the resource broker for more memory) by at least
278
  // 'bytes'. Returns false if not possible, true if the request succeeded. May allocate
279
  // more memory than was requested.
280
  // TODO: always returns false for now, not yet implemented.
281
6.92k
  bool ExpandLimit(int64_t /* unused: bytes */) { return false; }
282
283
  // Increases consumption of this tracker and its ancestors by 'bytes' only if
284
  // they can all consume 'bytes'. If this brings any of them over, none of them
285
  // are updated.
286
  // Returns true if the try succeeded.
287
  // In case of failure mem tracker that prevented consumption will be stored to
288
  // blocking_mem_tracker.
289
  bool TryConsume(int64_t bytes, MemTracker** blocking_mem_tracker = nullptr);
290
291
  // Decreases consumption of this tracker and its ancestors by 'bytes'.
292
  void Release(int64_t bytes);
293
294
  // Returns true if a valid limit of this tracker or one of its ancestors is
295
  // exceeded.
296
  bool AnyLimitExceeded();
297
298
  // If this tracker has a limit, checks the limit and attempts to free up some memory if
299
  // the limit is exceeded by calling any added GC functions. Returns true if the limit is
300
  // exceeded after calling the GC functions. Returns false if there is no limit.
301
  bool LimitExceeded();
302
303
  // Like LimitExceeded() but may also return true if the soft memory limit is exceeded.
304
  // The greater the excess, the higher the chance that it returns true.
305
  // If score is not 0, then it is used to determine positive result.
306
  SoftLimitExceededResult SoftLimitExceeded(double* score);
307
308
  SoftLimitExceededResult SoftLimitExceeded(double score) {
309
    return SoftLimitExceeded(&score);
310
  }
311
312
  // Combines the semantics of AnyLimitExceeded() and SoftLimitExceeded().
313
  //
314
  // Note: if there's more than one soft limit defined, the probability of it being
315
  // exceeded in at least one tracker is much higher (as each soft limit check is an
316
  // independent event).
317
  SoftLimitExceededResult AnySoftLimitExceeded(double* score);
318
319
32.7M
  SoftLimitExceededResult AnySoftLimitExceeded(double score) {
320
32.7M
    return AnySoftLimitExceeded(&score);
321
32.7M
  }
322
323
  // Returns the maximum consumption that can be made without exceeding the limit on
324
  // this tracker or any of its parents. Returns int64_t::max() if there are no
325
  // limits and a negative value if any limit is already exceeded.
326
  int64_t SpareCapacity() const;
327
328
329
467k
  int64_t limit() const { return limit_; }
330
44.4M
  bool has_limit() const { return limit_ >= 0; }
331
25.1M
  const std::string& id() const { return id_; }
332
333
  // Returns the memory consumed in bytes.
334
91.4M
  int64_t consumption() const {
335
91.4M
    return consumption_.current_value();
336
91.4M
  }
337
338
1.39M
  int64_t GetUpdatedConsumption(bool force = false) {
339
1.39M
    UpdateConsumption(force);
340
1.39M
    return consumption();
341
1.39M
  }
342
343
  // Note that if consumption_ is based on consumption_func_, this
344
  // will be the max value we've recorded in consumption(), not
345
  // necessarily the highest value consumption_func_ has ever
346
  // reached.
347
94
  int64_t peak_consumption() const { return consumption_.max_value(); }
348
349
  // Retrieve the parent tracker, or NULL If one is not set.
350
17.8M
  std::shared_ptr<MemTracker> parent() const { return parent_; }
351
352
  // Add a function 'f' to be called if the limit is reached.
353
  // 'f' does not need to be thread-safe as long as it is added to only one MemTracker.
354
  // Note that 'f' must be valid for the lifetime of this MemTracker.
355
43.4k
  void AddGarbageCollector(const std::shared_ptr<GarbageCollector>& gc) {
356
43.4k
    std::lock_guard<simple_spinlock> lock(gc_mutex_);
357
43.4k
    gcs_.push_back(gc);
358
43.4k
  }
359
360
  // Logs the usage of this tracker and all of its children (recursively).
361
  std::string LogUsage(
362
      const std::string& prefix = "", int64_t usage_threshold = 0, int indent = 0) const;
363
364
0
  void EnableLogging(bool enable, bool log_stack) {
365
0
    enable_logging_ = enable;
366
0
    log_stack_ = log_stack;
367
0
  }
368
369
  // Returns a textual representation of the tracker that is guaranteed to be
370
  // globally unique.
371
  std::string ToString() const;
372
373
  void SetMetricEntity(const scoped_refptr<MetricEntity>& metric_entity,
374
                       const std::string& name_suffix = std::string());
375
  scoped_refptr<MetricEntity> metric_entity() const;
376
377
1.32k
  bool add_to_parent() const {
378
1.32k
    return add_to_parent_;
379
1.32k
  }
380
381
  void SetPollChildrenConsumptionFunctors(
382
0
      PollChildrenConsumptionFunctors poll_children_consumption_functors) {
383
0
    poll_children_consumption_functors_ = std::move(poll_children_consumption_functors);
384
0
  }
385
386
 private:
387
41.9M
  bool CheckLimitExceeded() const {
388
41.9M
    return limit_ >= 0 && 
limit_ < consumption()41.9M
;
389
41.9M
  }
390
391
  // If consumption is higher than max_consumption, attempts to free memory by calling any
392
  // added GC functions.  Returns true if max_consumption is still exceeded. Takes
393
  // gc_lock. Updates metrics if initialized.
394
  bool GcMemory(int64_t max_consumption);
395
396
  // Called when the total release memory is larger than mem_tracker_tcmalloc_gc_release_bytes.
397
  // TcMalloc holds onto released memory and very slowly (if ever) releases it back to
398
  // the OS. This is problematic since it is memory we are not constantly tracking which
399
  // can cause us to go way over mem limits.
400
  void GcTcmalloc();
401
402
  // Logs the stack of the current consume/release. Used for debugging only.
403
  void LogUpdate(bool is_consume, int64_t bytes) const;
404
405
  // Variant of CreateTracker() that:
406
  // 1. Must be called with a non-NULL parent, and
407
  // 2. Must be called with parent->child_trackers_lock_ held.
408
  std::shared_ptr<MemTracker> CreateChild(
409
      int64_t byte_limit,
410
      const std::string& id,
411
      ConsumptionFunctor consumption_functor,
412
      MayExist may_exist,
413
      AddToParent add_to_parent,
414
      CreateMetrics create_metrics);
415
416
  // Variant of FindTracker() that:
417
  // 1. Must be called with a non-NULL parent, and
418
  // 2. Must be called with parent->child_trackers_lock_ held.
419
  MemTrackerPtr FindChildUnlocked(const std::string& id);
420
421
  // Creates the root tracker.
422
  static void CreateRootTracker();
423
424
  const int64_t limit_;
425
  const int64_t soft_limit_;
426
  const std::string id_;
427
  const ConsumptionFunctor consumption_functor_;
428
  PollChildrenConsumptionFunctors poll_children_consumption_functors_;
429
  const std::string descr_;
430
  std::shared_ptr<MemTracker> parent_;
431
  CoarseMonoClock::time_point last_consumption_update_ = CoarseMonoClock::time_point::min();
432
433
  class TrackerMetrics;
434
  std::unique_ptr<TrackerMetrics> metrics_;
435
436
  HighWaterMark consumption_{0};
437
438
  // this tracker plus all of its ancestors
439
  std::vector<MemTracker*> all_trackers_;
440
  // all_trackers_ with valid limits
441
  std::vector<MemTracker*> limit_trackers_;
442
443
  // All the child trackers of this tracker. Used for error reporting and
444
  // listing only (i.e. updating the consumption of a parent tracker does not
445
  // update that of its children).
446
  // Note: This lock must never be taken on a parent before on a child.
447
  // It is taken in the opposite order during UnregisterFromParentIfNoChildren()
448
  mutable std::mutex child_trackers_mutex_;
449
  std::unordered_map<std::string, std::weak_ptr<MemTracker>> child_trackers_;
450
451
  simple_spinlock gc_mutex_;
452
453
  // Functions to call after the limit is reached to free memory.
454
  std::vector<std::weak_ptr<GarbageCollector>> gcs_;
455
456
  ThreadSafeRandom rand_;
457
458
  // If true, logs to INFO every consume/release called. Used for debugging.
459
  bool enable_logging_;
460
461
  // If true, log the stack as well.
462
  bool log_stack_;
463
464
  AddToParent add_to_parent_;
465
};
466
467
// An std::allocator that manipulates a MemTracker during allocation
468
// and deallocation.
469
template<typename T, typename Alloc = std::allocator<T> >
470
class MemTrackerAllocator : public Alloc {
471
 public:
472
  typedef typename Alloc::pointer pointer;
473
  typedef typename Alloc::const_pointer const_pointer;
474
  typedef typename Alloc::size_type size_type;
475
476
  explicit MemTrackerAllocator(std::shared_ptr<MemTracker> mem_tracker)
477
      : mem_tracker_(std::move(mem_tracker)) {}
478
479
  // This constructor is used for rebinding.
480
  template <typename U>
481
  MemTrackerAllocator(const MemTrackerAllocator<U>& allocator)
482
      : Alloc(allocator),
483
        mem_tracker_(allocator.mem_tracker()) {
484
  }
485
486
  ~MemTrackerAllocator() {
487
  }
488
489
  pointer allocate(size_type n, const_pointer hint = 0) {
490
    // Ideally we'd use TryConsume() here to enforce the tracker's limit.
491
    // However, that means throwing bad_alloc if the limit is exceeded, and
492
    // it's not clear that the rest of YB can handle that.
493
    mem_tracker_->Consume(n * sizeof(T));
494
    return Alloc::allocate(n, hint);
495
  }
496
497
  void deallocate(pointer p, size_type n) {
498
    Alloc::deallocate(p, n);
499
    mem_tracker_->Release(n * sizeof(T));
500
  }
501
502
  // This allows an allocator<T> to be used for a different type.
503
  template <class U>
504
  struct rebind {
505
    typedef MemTrackerAllocator<U, typename Alloc::template rebind<U>::other> other;
506
  };
507
508
  const std::shared_ptr<MemTracker>& mem_tracker() const { return mem_tracker_; }
509
510
 private:
511
  std::shared_ptr<MemTracker> mem_tracker_;
512
};
513
514
YB_STRONGLY_TYPED_BOOL(AlreadyConsumed);
515
516
// Convenience class that adds memory consumption to a tracker when declared,
517
// releasing it when the end of scope is reached.
518
class ScopedTrackedConsumption {
519
 public:
520
555M
  ScopedTrackedConsumption() : consumption_(0) {}
521
522
  ScopedTrackedConsumption(MemTrackerPtr tracker,
523
                           int64_t to_consume,
524
                           AlreadyConsumed already_consumed = AlreadyConsumed::kFalse)
525
325M
      : tracker_(std::move(tracker)), consumption_(to_consume) {
526
325M
    DCHECK(*this);
527
325M
    if (!already_consumed) {
528
325M
      tracker_->Consume(consumption_);
529
325M
    }
530
325M
  }
531
532
  ScopedTrackedConsumption(const ScopedTrackedConsumption&) = delete;
533
  void operator=(const ScopedTrackedConsumption&) = delete;
534
535
  ScopedTrackedConsumption(ScopedTrackedConsumption&& rhs)
536
50.9M
      : tracker_(std::move(rhs.tracker_)), consumption_(rhs.consumption_) {
537
50.9M
    rhs.consumption_ = 0;
538
50.9M
  }
539
540
501M
  void operator=(ScopedTrackedConsumption&& rhs) {
541
501M
    if (rhs) {
542
318M
      DCHECK(!*this);
543
318M
      tracker_ = std::move(rhs.tracker_);
544
318M
      consumption_ = rhs.consumption_;
545
318M
      rhs.consumption_ = 0;
546
318M
    } else 
if (183M
tracker_183M
) {
547
72.5M
      tracker_->Release(consumption_);
548
72.5M
      tracker_ = nullptr;
549
72.5M
    }
550
501M
  }
551
552
  void Reset(int64_t new_consumption) {
553
    // Consume(-x) is the same as Release(x).
554
    tracker_->Consume(new_consumption - consumption_);
555
    consumption_ = new_consumption;
556
  }
557
558
0
  void Swap(ScopedTrackedConsumption* rhs) {
559
0
    std::swap(tracker_, rhs->tracker_);
560
0
    std::swap(consumption_, rhs->consumption_);
561
0
  }
562
563
1.15G
  explicit operator bool() const {
564
1.15G
    return tracker_ != nullptr;
565
1.15G
  }
566
567
930M
  ~ScopedTrackedConsumption() {
568
930M
    if (tracker_) {
569
251M
      tracker_->Release(consumption_);
570
251M
    }
571
930M
  }
572
573
75.0M
  void Add(int64_t delta) {
574
75.0M
    tracker_->Consume(delta);
575
75.0M
    consumption_ += delta;
576
75.0M
  }
577
578
  int64_t consumption() const { return consumption_; }
579
580
70.3M
  const MemTrackerPtr& mem_tracker() { return tracker_; }
581
582
 private:
583
  MemTrackerPtr tracker_;
584
  int64_t consumption_;
585
};
586
587
template <class F>
588
123k
int64_t AbsRelMemLimit(int64_t value, const F& f) {
589
123k
  if (value < 0) {
590
61.8k
    auto base_memory_limit = f();
591
61.8k
    if (base_memory_limit < 0) {
592
0
      return -1;
593
0
    }
594
61.8k
    return base_memory_limit * std::min<int64_t>(-value, 100) / 100;
595
61.8k
  }
596
61.8k
  if (value == 0) {
597
61.8k
    return -1;
598
61.8k
  }
599
1
  return value;
600
61.8k
}
connection_context.cc:long long yb::AbsRelMemLimit<yb::rpc::ConnectionContextFactory::ConnectionContextFactory(long long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<yb::MemTracker> const&)::$_0>(long long, yb::rpc::ConnectionContextFactory::ConnectionContextFactory(long long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<yb::MemTracker> const&)::$_0 const&)
Line
Count
Source
588
61.8k
int64_t AbsRelMemLimit(int64_t value, const F& f) {
589
61.8k
  if (value < 0) {
590
61.8k
    auto base_memory_limit = f();
591
61.8k
    if (base_memory_limit < 0) {
592
0
      return -1;
593
0
    }
594
61.8k
    return base_memory_limit * std::min<int64_t>(-value, 100) / 100;
595
61.8k
  }
596
0
  if (value == 0) {
597
0
    return -1;
598
0
  }
599
0
  return value;
600
0
}
connection_context.cc:long long yb::AbsRelMemLimit<yb::rpc::ConnectionContextFactory::ConnectionContextFactory(long long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<yb::MemTracker> const&)::$_1>(long long, yb::rpc::ConnectionContextFactory::ConnectionContextFactory(long long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::shared_ptr<yb::MemTracker> const&)::$_1 const&)
Line
Count
Source
588
61.8k
int64_t AbsRelMemLimit(int64_t value, const F& f) {
589
61.8k
  if (value < 0) {
590
0
    auto base_memory_limit = f();
591
0
    if (base_memory_limit < 0) {
592
0
      return -1;
593
0
    }
594
0
    return base_memory_limit * std::min<int64_t>(-value, 100) / 100;
595
0
  }
596
61.8k
  if (value == 0) {
597
61.8k
    return -1;
598
61.8k
  }
599
1
  return value;
600
61.8k
}
601
602
struct MemTrackerData {
603
  MemTrackerPtr tracker;
604
  // Depth of this tracker in hierarchy, i.e. root have depth = 0, his children 1 and so on.
605
  int depth = 0;
606
  // Some mem trackers does not report their consumption to parent, so their consumption does not
607
  // participate in limit calculation or parent. We accumulate such consumption in field below.
608
  size_t consumption_excluded_from_ancestors = 0;
609
};
610
611
const MemTrackerData& CollectMemTrackerData(const MemTrackerPtr& tracker, int depth,
612
                                            std::vector<MemTrackerData>* output);
613
614
std::string DumpMemoryUsage();
615
616
// Checks whether it is ok to proceed with action having specified score under current memory
617
// conditions.
618
// Returns true when action should proceed, false if it should be rejected.
619
// score - score to reject action, should be in the range (0.0, 1.0],
620
// the higher the score - the greater probability that action should be rejected.
621
// Score 0.0 is a special value, meaning that random value should be picked for score.
622
//
623
// Suppose we have soft limit A and hard limit B. Where A < B.
624
// And current memory usage X.
625
//
626
// If X < A => this function always return true.
627
// If X >= B => this function always return false.
628
// If A < X < B, then we reject if used score > (B - X) / (B - A).
629
bool CheckMemoryPressureWithLogging(
630
    const MemTrackerPtr& mem_tracker, double score, const char* error_prefix);
631
632
} // namespace yb
633
634
#endif // YB_UTIL_MEM_TRACKER_H