/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 |