LCOV - code coverage report
Current view: top level - source/common/stats - stat_merger.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 37 80 46.2 %
Date: 2024-01-05 06:35:25 Functions: 1 7 14.3 %

          Line data    Source code
       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         247 :                                                          const DynamicsMap& map) {
      24         247 :   auto iter = map.find(name);
      25         247 :   if (iter == map.end()) {
      26          25 :     return symbolic_pool_.add(name);
      27          25 :   }
      28             : 
      29         222 :   const DynamicSpans& dynamic_spans = iter->second;
      30         222 :   auto dynamic = dynamic_spans.begin();
      31         222 :   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         222 :   StatNameVec segments;
      36         222 :   uint32_t segment_index = 0;
      37         222 :   std::vector<absl::string_view> dynamic_segments;
      38             : 
      39        5372 :   for (auto segment : absl::StrSplit(name, '.')) {
      40        5372 :     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        1374 :       ASSERT(dynamic_segments.empty());
      45        1374 :       if (dynamic->second == segment_index) {
      46             :         // Handle start==end (a one-segment span).
      47        1301 :         segments.push_back(dynamic_pool_.add(segment));
      48        1301 :         ++dynamic;
      49        1301 :       } else {
      50             :         // Handle start<end, so we save the first segment in dynamic_segments.
      51          73 :         dynamic_segments.push_back(segment);
      52          73 :       }
      53        3998 :     } else if (dynamic_segments.empty()) {
      54             :       // Handle that we are not in dynamic mode; we are just allocating
      55             :       // a symbolic segment.
      56        3913 :       segments.push_back(symbolic_pool_.add(segment));
      57        3913 :     } else {
      58             :       // Handle the next dynamic segment.
      59          85 :       dynamic_segments.push_back(segment);
      60          85 :       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          73 :         segments.push_back(dynamic_pool_.add(absl::StrJoin(dynamic_segments, ".")));
      64          73 :         dynamic_segments.clear();
      65          73 :         ++dynamic;
      66          73 :       }
      67          85 :     }
      68        5372 :     ++segment_index;
      69        5372 :   }
      70         222 :   ASSERT(dynamic_segments.empty());
      71         222 :   ASSERT(dynamic == dynamic_end);
      72             : 
      73         222 :   storage_ptr_ = symbol_table_.join(segments);
      74         222 :   return StatName(storage_ptr_.get());
      75         247 : }
      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

Generated by: LCOV version 1.15