Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/common/stats/deferred_creation.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include "envoy/common/pure.h"
4
#include "envoy/stats/scope.h"
5
#include "envoy/stats/stats.h"
6
7
#include "source/common/common/cleanup.h"
8
#include "source/common/common/thread.h"
9
#include "source/common/stats/symbol_table.h"
10
#include "source/common/stats/utility.h"
11
12
namespace Envoy {
13
namespace Stats {
14
15
/**
16
 * Lazy-initialization wrapper for StatsStructType, intended for deferred instantiation of a block
17
 * of stats that might not be needed in a given Envoy process.
18
 *
19
 * This class is thread-safe -- instantiations can occur on multiple concurrent threads.
20
 * This is used when
21
 * :ref:`enable_deferred_creation_stats
22
 * <envoy_v3_api_field_config.bootstrap.v3.Bootstrap.deferred_stat_options>` is enabled.
23
 */
24
template <typename StatsStructType>
25
class DeferredStats : public DeferredCreationCompatibleInterface<StatsStructType> {
26
public:
27
  // Capture the stat names object and the scope with a ctor, that can be used to instantiate a
28
  // StatsStructType object later.
29
  // Caller should make sure scope and stat_names outlive this object.
30
  DeferredStats(const typename StatsStructType::StatNameType& stat_names,
31
                Stats::ScopeSharedPtr scope)
32
      : initialized_(
33
            // A lambda is used as we need to register the name into the symbol table.
34
            // Note: there is no issue to capture a reference of the scope here as this lambda is
35
            // only used to initialize the 'initialized_' Gauge.
36
0
            [&scope]() -> Gauge& {
37
0
              Stats::StatNamePool pool(scope->symbolTable());
38
0
              return Stats::Utility::gaugeFromElements(
39
0
                  *scope, {pool.add(StatsStructType::typeName()), pool.add("initialized")},
40
0
                  Stats::Gauge::ImportMode::HiddenAccumulate);
41
0
            }()),
42
0
        ctor_([this, &stat_names, stats_scope = std::move(scope)]() -> StatsStructType* {
43
0
          initialized_.inc();
44
          // Reset ctor_ to save some RAM.
45
0
          Cleanup reset_ctor([this] { ctor_ = nullptr; });
46
0
          return new StatsStructType(stat_names, *stats_scope);
47
0
        }) {
48
0
    if (initialized_.value() > 0) {
49
0
      getOrCreateHelper();
50
0
    }
51
0
  }
52
0
  ~DeferredStats() override {
53
0
    if (ctor_ == nullptr) {
54
0
      initialized_.dec();
55
0
    }
56
0
  }
57
0
  inline StatsStructType& getOrCreate() override { return getOrCreateHelper(); }
58
0
  bool isPresent() const override { return !internal_stats_.isNull(); }
59
60
private:
61
  // We can't call getOrCreate directly from constructor, otherwise the compiler complains about
62
  // bypassing virtual dispatch even though it's fine.
63
0
  inline StatsStructType& getOrCreateHelper() { return *internal_stats_.get(ctor_); }
64
65
  // In order to preserve stat value continuity across a config reload, we need to automatically
66
  // re-instantiate lazy stats when they are constructed, if there is already a live instantiation
67
  // to the same stats. Consider the following alternate scenarios:
68
69
  // Scenario 1: a cluster is instantiated but receives no requests, so its traffic-related stats
70
  // are never instantiated. When this cluster gets reloaded on a config update, a new lazy-init
71
  // block is created, but the stats should again not be instantiated.
72
73
  // Scenario 2: a cluster is instantiated and receives traffic, so its traffic-related stats are
74
  // instantiated. We must ensure that a new instance for the same cluster gets its lazy-stats
75
  // instantiated before the previous cluster of the same name is destructed.
76
77
  // To do that we keep an "initialized" gauge in the cluster's scope, which will be associated by
78
  // name to the previous generation's cluster's lazy-init block. We use the value in this shared
79
  // gauge to determine whether to instantiate the lazy block on construction.
80
  Gauge& initialized_;
81
  // TODO(#26957): Clean up this ctor_ by moving its ownership to AtomicPtr, and drop
82
  // the setter lambda when the nested object is created.
83
  std::function<StatsStructType*()> ctor_;
84
  Thread::AtomicPtr<StatsStructType, Thread::AtomicPtrAllocMode::DeleteOnDestruct> internal_stats_;
85
};
86
87
// Non-deferred wrapper over StatsStructType. This is used when
88
// :ref:`enable_deferred_creation_stats
89
// <envoy_v3_api_field_config.bootstrap.v3.Bootstrap.deferred_stat_options>` is not enabled.
90
template <typename StatsStructType>
91
class DirectStats : public DeferredCreationCompatibleInterface<StatsStructType> {
92
public:
93
  DirectStats(const typename StatsStructType::StatNameType& stat_names, Stats::Scope& scope)
94
403k
      : stats_(stat_names, scope) {}
95
38.6k
  inline StatsStructType& getOrCreate() override { return stats_; }
96
0
  bool isPresent() const override { return true; }
97
98
private:
99
  StatsStructType stats_;
100
};
101
102
// Template that lazily initializes a StatsStruct.
103
// The bootstrap config :ref:`enable_deferred_creation_stats
104
// <envoy_v3_api_field_config.bootstrap.v3.Bootstrap.deferred_stat_options>` decides if
105
// stats lazy initialization is enabled or not.
106
template <typename StatsStructType>
107
DeferredCreationCompatibleStats<StatsStructType>
108
createDeferredCompatibleStats(Stats::ScopeSharedPtr scope,
109
                              const typename StatsStructType::StatNameType& stat_names,
110
403k
                              bool defer_creation) {
111
403k
  if (defer_creation) {
112
0
    return DeferredCreationCompatibleStats<StatsStructType>(
113
0
        std::make_unique<DeferredStats<StatsStructType>>(stat_names, scope));
114
403k
  } else {
115
403k
    return DeferredCreationCompatibleStats<StatsStructType>(
116
403k
        std::make_unique<DirectStats<StatsStructType>>(stat_names, *scope));
117
403k
  }
118
403k
}
119
120
} // namespace Stats
121
} // namespace Envoy