/proc/self/cwd/source/common/stats/stat_merger.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/common/stats/stat_merger.h" |
2 | | |
3 | | #include <algorithm> |
4 | | |
5 | | namespace Envoy { |
6 | | namespace Stats { |
7 | | |
8 | 0 | StatMerger::StatMerger(Store& target_store) : temp_scope_(target_store.createScope("")) {} |
9 | | |
10 | 0 | StatMerger::~StatMerger() { |
11 | | // By the time a parent exits, all its contributions to accumulated gauges |
12 | | // should be zero. But depending on the timing of the stat-merger |
13 | | // communication shutdown and other shutdown activities on the parent, the |
14 | | // gauges may not all be zero yet. So simply erase all the parent |
15 | | // contributions. |
16 | 0 | for (StatName stat_name : parent_gauges_) { |
17 | 0 | Gauge& gauge = temp_scope_->gaugeFromStatName(stat_name, Gauge::ImportMode::Uninitialized); |
18 | 0 | gauge.setParentValue(0); |
19 | 0 | } |
20 | 0 | } |
21 | | |
22 | | StatName StatMerger::DynamicContext::makeDynamicStatName(const std::string& name, |
23 | 797 | const DynamicsMap& map) { |
24 | 797 | auto iter = map.find(name); |
25 | 797 | if (iter == map.end()) { |
26 | 251 | return symbolic_pool_.add(name); |
27 | 251 | } |
28 | | |
29 | 546 | const DynamicSpans& dynamic_spans = iter->second; |
30 | 546 | auto dynamic = dynamic_spans.begin(); |
31 | 546 | auto dynamic_end = dynamic_spans.end(); |
32 | | |
33 | | // Name has embedded dynamic segments; we'll need to join together the |
34 | | // static/dynamic StatName segments. |
35 | 546 | StatNameVec segments; |
36 | 546 | uint32_t segment_index = 0; |
37 | 546 | std::vector<absl::string_view> dynamic_segments; |
38 | | |
39 | 20.2M | for (auto segment : absl::StrSplit(name, '.')) { |
40 | 20.2M | if (dynamic != dynamic_end && dynamic->first == segment_index) { |
41 | | // Handle start of dynamic span. We note that we are in a dynamic |
42 | | // span by adding to dynamic_segments, which should of course be |
43 | | // non-empty. |
44 | 10.7M | ASSERT(dynamic_segments.empty()); |
45 | 10.7M | if (dynamic->second == segment_index) { |
46 | | // Handle start==end (a one-segment span). |
47 | 9.34M | segments.push_back(dynamic_pool_.add(segment)); |
48 | 9.34M | ++dynamic; |
49 | 9.34M | } else { |
50 | | // Handle start<end, so we save the first segment in dynamic_segments. |
51 | 1.44M | dynamic_segments.push_back(segment); |
52 | 1.44M | } |
53 | 10.7M | } else if (dynamic_segments.empty()) { |
54 | | // Handle that we are not in dynamic mode; we are just allocating |
55 | | // a symbolic segment. |
56 | 6.02M | segments.push_back(symbolic_pool_.add(segment)); |
57 | 6.02M | } else { |
58 | | // Handle the next dynamic segment. |
59 | 3.46M | dynamic_segments.push_back(segment); |
60 | 3.46M | if (dynamic->second == segment_index) { |
61 | | // Handle that this dynamic segment is the last one, and we're flipping |
62 | | // back to symbolic mode. |
63 | 1.44M | segments.push_back(dynamic_pool_.add(absl::StrJoin(dynamic_segments, "."))); |
64 | 1.44M | dynamic_segments.clear(); |
65 | 1.44M | ++dynamic; |
66 | 1.44M | } |
67 | 3.46M | } |
68 | 20.2M | ++segment_index; |
69 | 20.2M | } |
70 | 546 | ASSERT(dynamic_segments.empty()); |
71 | 546 | ASSERT(dynamic == dynamic_end); |
72 | | |
73 | 546 | storage_ptr_ = symbol_table_.join(segments); |
74 | 546 | return StatName(storage_ptr_.get()); |
75 | 546 | } |
76 | | |
77 | | void StatMerger::mergeCounters(const Protobuf::Map<std::string, uint64_t>& counter_deltas, |
78 | 0 | const DynamicsMap& dynamic_map) { |
79 | 0 | for (const auto& counter : counter_deltas) { |
80 | 0 | const std::string& name = counter.first; |
81 | 0 | StatMerger::DynamicContext dynamic_context(temp_scope_->symbolTable()); |
82 | 0 | StatName stat_name = dynamic_context.makeDynamicStatName(name, dynamic_map); |
83 | 0 | temp_scope_->counterFromStatName(stat_name).add(counter.second); |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | void StatMerger::mergeGauges(const Protobuf::Map<std::string, uint64_t>& gauges, |
88 | 0 | const DynamicsMap& dynamic_map) { |
89 | 0 | for (const auto& gauge : gauges) { |
90 | | // Merging gauges via RPC from the parent has 3 cases; case 1 and 3b are the |
91 | | // most common. |
92 | | // |
93 | | // 1. Child thinks gauge is Accumulate : data is combined in |
94 | | // gauge_ref.add() below. |
95 | | // 2. Child thinks gauge is NeverImport: we skip this loop entry via |
96 | | // 'continue'. |
97 | | // 3. Child has not yet initialized gauge yet -- this merge is the |
98 | | // first time the child learns of the gauge. It's possible the child |
99 | | // will think the gauge is NeverImport due to a code change. But for |
100 | | // now we will leave the gauge in the child process as |
101 | | // import_mode==Uninitialized, and accumulate the parent value in |
102 | | // gauge_ref.add(). Gauges in this mode will be included in |
103 | | // stats-sinks or the admin /stats calls, until the child initializes |
104 | | // the gauge, in which case: |
105 | | // 3a. Child later initializes gauges as NeverImport: the parent value is |
106 | | // cleared during the mergeImportMode call. |
107 | | // 3b. Child later initializes gauges as Accumulate: the parent value is |
108 | | // retained. |
109 | |
|
110 | 0 | StatMerger::DynamicContext dynamic_context(temp_scope_->symbolTable()); |
111 | 0 | StatName stat_name = dynamic_context.makeDynamicStatName(gauge.first, dynamic_map); |
112 | 0 | GaugeOptConstRef gauge_opt = temp_scope_->findGauge(stat_name); |
113 | |
|
114 | 0 | Gauge::ImportMode import_mode = Gauge::ImportMode::Uninitialized; |
115 | 0 | if (gauge_opt) { |
116 | 0 | import_mode = gauge_opt->get().importMode(); |
117 | 0 | if (import_mode == Gauge::ImportMode::NeverImport) { |
118 | 0 | continue; |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | | // TODO(snowp): Propagate tag values during hot restarts. |
123 | 0 | auto& gauge_ref = temp_scope_->gaugeFromStatName(stat_name, import_mode); |
124 | 0 | if (gauge_ref.importMode() == Gauge::ImportMode::NeverImport) { |
125 | | // On the first iteration through the loop, the gauge will not be loaded into the scope |
126 | | // cache even though it might exist in another scope. Thus, we need to check again for |
127 | | // the import status to see if we should skip this gauge. |
128 | | // |
129 | | // TODO(mattklein123): There is a race condition here. It's technically possible that |
130 | | // between the time we created this stat, the stat might be created by the child as a |
131 | | // never import stat, making the below math invalid. A follow up solution is to take the |
132 | | // store lock starting from gaugeFromStatName() to the end of this function, but this will |
133 | | // require adding some type of mergeGauge() function to the scope and dealing with recursive |
134 | | // lock acquisition, etc. so we will leave this as a follow up. This race should be incredibly |
135 | | // rare. |
136 | 0 | continue; |
137 | 0 | } |
138 | | |
139 | 0 | const uint64_t new_parent_value = gauge.second; |
140 | 0 | parent_gauges_.insert(gauge_ref.statName()); |
141 | 0 | gauge_ref.setParentValue(new_parent_value); |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | 0 | void StatMerger::retainParentGaugeValue(Stats::StatName gauge_name) { |
146 | 0 | parent_gauges_.erase(gauge_name); |
147 | 0 | } |
148 | | |
149 | | void StatMerger::mergeStats(const Protobuf::Map<std::string, uint64_t>& counter_deltas, |
150 | | const Protobuf::Map<std::string, uint64_t>& gauges, |
151 | 0 | const DynamicsMap& dynamics) { |
152 | 0 | mergeCounters(counter_deltas, dynamics); |
153 | 0 | mergeGauges(gauges, dynamics); |
154 | 0 | } |
155 | | |
156 | | } // namespace Stats |
157 | | } // namespace Envoy |