1
#pragma once
2

            
3
#include <algorithm>
4
#include <cstring>
5
#include <functional>
6
#include <string>
7

            
8
#include "envoy/stats/stats.h"
9
#include "envoy/stats/stats_matcher.h"
10
#include "envoy/stats/store.h"
11

            
12
#include "source/common/common/utility.h"
13
#include "source/common/stats/allocator.h"
14
#include "source/common/stats/histogram_impl.h"
15
#include "source/common/stats/null_counter.h"
16
#include "source/common/stats/null_gauge.h"
17
#include "source/common/stats/null_text_readout.h"
18
#include "source/common/stats/symbol_table.h"
19
#include "source/common/stats/tag_utility.h"
20
#include "source/common/stats/utility.h"
21

            
22
#include "absl/container/flat_hash_map.h"
23

            
24
namespace Envoy {
25
namespace Stats {
26

            
27
// Isolated implementation of Stats::Store. This class is not thread-safe by
28
// itself, but a thread-safe wrapper can be built, e.g. TestIsolatedStoreImpl
29
// in test/integration/server.h.
30
class IsolatedStoreImpl : public Store {
31
public:
32
  IsolatedStoreImpl();
33
  explicit IsolatedStoreImpl(SymbolTable& symbol_table);
34
  ~IsolatedStoreImpl() override;
35

            
36
  // Stats::Store
37
  const SymbolTable& constSymbolTable() const override { return alloc_.constSymbolTable(); }
38
122501029
  SymbolTable& symbolTable() override { return alloc_.symbolTable(); }
39

            
40
10882209
  void deliverHistogramToSinks(const Histogram&, uint64_t) override {}
41
  ScopeSharedPtr rootScope() override;
42
  ConstScopeSharedPtr constRootScope() const override;
43
2324
  std::vector<CounterSharedPtr> counters() const override { return counters_.toVector(); }
44
3404
  std::vector<GaugeSharedPtr> gauges() const override {
45
    // TODO(jmarantz): should we filter out gauges where
46
    // gauge.importMode() != Gauge::ImportMode::Uninitialized ?
47
    // I don't think this matters because that should only occur for gauges
48
    // received in a hot-restart transfer, and isolated-store gauges should
49
    // never be transmitted that way.
50
3404
    return gauges_.toVector();
51
3404
  }
52
  std::vector<ParentHistogramSharedPtr> histograms() const override {
53
    return std::vector<ParentHistogramSharedPtr>{};
54
  }
55
1
  std::vector<TextReadoutSharedPtr> textReadouts() const override {
56
1
    return text_readouts_.toVector();
57
1
  }
58

            
59
76
  void forEachCounter(SizeFn f_size, StatFn<Counter> f_stat) const override {
60
76
    counters_.forEachStat(f_size, f_stat);
61
76
  }
62

            
63
76
  void forEachGauge(SizeFn f_size, StatFn<Gauge> f_stat) const override {
64
76
    gauges_.forEachStat(f_size, f_stat);
65
76
  }
66

            
67
71
  void forEachTextReadout(SizeFn f_size, StatFn<TextReadout> f_stat) const override {
68
71
    text_readouts_.forEachStat(f_size, f_stat);
69
71
  }
70

            
71
  void forEachHistogram(SizeFn f_size, StatFn<ParentHistogram> f_stat) const override {
72
    UNREFERENCED_PARAMETER(f_size);
73
    UNREFERENCED_PARAMETER(f_stat);
74
  }
75

            
76
4
  void forEachScope(SizeFn f_size, StatFn<const Scope> f_stat) const override {
77
4
    if (f_size != nullptr) {
78
4
      f_size(scopes_.size() + 1);
79
4
    }
80
4
    f_stat(*constRootScope());
81
10
    for (const ScopeSharedPtr& scope : scopes_) {
82
10
      f_stat(*scope);
83
10
    }
84
4
  }
85

            
86
  void evictUnused() override {
87
    // Do nothing. Eviction is only supported on the thread local stores.
88
  }
89

            
90
76
  void forEachSinkedCounter(SizeFn f_size, StatFn<Counter> f_stat) const override {
91
76
    forEachCounter(f_size, f_stat);
92
76
  }
93

            
94
76
  void forEachSinkedGauge(SizeFn f_size, StatFn<Gauge> f_stat) const override {
95
76
    forEachGauge(f_size, f_stat);
96
76
  }
97

            
98
72
  void forEachSinkedTextReadout(SizeFn f_size, StatFn<TextReadout> f_stat) const override {
99
72
    forEachTextReadout(f_size, f_stat);
100
72
  }
101

            
102
71
  void forEachSinkedHistogram(SizeFn f_size, StatFn<ParentHistogram> f_stat) const override {
103
71
    UNREFERENCED_PARAMETER(f_size);
104
71
    UNREFERENCED_PARAMETER(f_stat);
105
71
  }
106

            
107
1
  NullCounterImpl& nullCounter() override { return null_counter_; }
108
10021
  NullGaugeImpl& nullGauge() override { return null_gauge_; }
109

            
110
4
  bool iterate(const IterateFn<Counter>& fn) const override {
111
4
    return constRootScope()->iterate(fn);
112
4
  }
113
4
  bool iterate(const IterateFn<Gauge>& fn) const override { return constRootScope()->iterate(fn); }
114
2
  bool iterate(const IterateFn<Histogram>& fn) const override {
115
2
    return constRootScope()->iterate(fn);
116
2
  }
117
2
  bool iterate(const IterateFn<TextReadout>& fn) const override {
118
2
    return constRootScope()->iterate(fn);
119
2
  }
120

            
121
  void extractAndAppendTags(StatName, StatNamePool&, StatNameTagVector&) override {}
122
  void extractAndAppendTags(absl::string_view, StatNamePool&, StatNameTagVector&) override {}
123
1
  const TagVector& fixedTags() override { CONSTRUCT_ON_FIRST_USE(TagVector); }
124

            
125
protected:
126
  /**
127
   * Provides a hook for sub-classes to define how to create new scopes. When
128
   * subclassing IsolatedStoreImpl you likely want to also subclass
129
   * IsolatedScopeImpl. Overriding this method enables scopes of the appropriate
130
   * type to be created from Store::rootScope(), Scope::scopeFromStatName, and
131
   * Scope::createScope, without needing to override those. makeScope is usually
132
   * implemented by "return std::make_shared<YourScopeType>(name, *this)".
133
   *
134
   * @param name the fully qualified stat name -- no further prefixing needed.
135
   */
136
  virtual ScopeSharedPtr makeScope(StatName name, StatsMatcherSharedPtr matcher = nullptr);
137

            
138
private:
139
  /**
140
   * A stats cache template that is used by the isolated store.
141
   */
142
  template <class Base> class IsolatedStatsCache {
143
  public:
144
    using CounterAllocator = std::function<RefcountPtr<Base>(
145
        const TagUtility::TagStatNameJoiner& joiner, StatNameTagVectorOptConstRef tags)>;
146
    using GaugeAllocator =
147
        std::function<RefcountPtr<Base>(const TagUtility::TagStatNameJoiner& joiner,
148
                                        StatNameTagVectorOptConstRef tags, Gauge::ImportMode)>;
149
    using HistogramAllocator =
150
        std::function<RefcountPtr<Base>(const TagUtility::TagStatNameJoiner& joiner,
151
                                        StatNameTagVectorOptConstRef tags, Histogram::Unit)>;
152
    using TextReadoutAllocator =
153
        std::function<RefcountPtr<Base>(const TagUtility::TagStatNameJoiner& joiner,
154
                                        StatNameTagVectorOptConstRef tags, TextReadout::Type)>;
155
    using BaseOptConstRef = absl::optional<std::reference_wrapper<const Base>>;
156

            
157
1825781
    IsolatedStatsCache(CounterAllocator alloc) : counter_alloc_(alloc) {}
158
1825781
    IsolatedStatsCache(GaugeAllocator alloc) : gauge_alloc_(alloc) {}
159
1825781
    IsolatedStatsCache(HistogramAllocator alloc) : histogram_alloc_(alloc) {}
160
1825781
    IsolatedStatsCache(TextReadoutAllocator alloc) : text_readout_alloc_(alloc) {}
161

            
162
    OptRef<Base> get(StatName prefix, StatName basename, StatNameTagVectorOptConstRef tags,
163
19228283
                     SymbolTable& symbol_table, OptRef<const StatsMatcher> matcher = {}) {
164
19228283
      TagUtility::TagStatNameJoiner joiner(prefix, basename, tags, symbol_table);
165
19228283
      StatName name = joiner.nameWithTags();
166

            
167
      // If we have a matcher and it rejects this stat, we return nullopt.
168
19228283
      if (matcher.has_value() && matcher->rejects(name)) {
169
3
        return {};
170
3
      }
171

            
172
19228280
      auto stat = stats_.find(name);
173
19228280
      if (stat != stats_.end()) {
174
19632
        return *stat->second;
175
19632
      }
176

            
177
19208648
      RefcountPtr<Base> new_stat = counter_alloc_(joiner, tags);
178
19208648
      stats_.emplace(new_stat->statName(), new_stat);
179
19208648
      return *new_stat;
180
19228280
    }
181

            
182
    OptRef<Base> get(StatName prefix, StatName basename, StatNameTagVectorOptConstRef tags,
183
                     SymbolTable& symbol_table, Gauge::ImportMode import_mode,
184
5441880
                     OptRef<const StatsMatcher> matcher = {}) {
185
5441880
      TagUtility::TagStatNameJoiner joiner(prefix, basename, tags, symbol_table);
186
5441880
      StatName name = joiner.nameWithTags();
187

            
188
      // If we have a matcher and it rejects this stat, we return nullopt.
189
5441880
      if (matcher.has_value() && import_mode != Gauge::ImportMode::HiddenAccumulate &&
190
5441880
          matcher->rejects(name)) {
191
2
        return {};
192
2
      }
193

            
194
5441878
      auto stat = stats_.find(name);
195
5441878
      if (stat != stats_.end()) {
196
2053
        return *stat->second;
197
2053
      }
198

            
199
5439825
      RefcountPtr<Base> new_stat = gauge_alloc_(joiner, tags, import_mode);
200
5439825
      stats_.emplace(new_stat->statName(), new_stat);
201
5439825
      return *new_stat;
202
5441878
    }
203

            
204
    OptRef<Base> get(StatName prefix, StatName basename, StatNameTagVectorOptConstRef tags,
205
                     SymbolTable& symbol_table, Histogram::Unit unit,
206
2588879
                     OptRef<const StatsMatcher> matcher = {}) {
207
2588879
      TagUtility::TagStatNameJoiner joiner(prefix, basename, tags, symbol_table);
208
2588879
      StatName name = joiner.nameWithTags();
209

            
210
      // If we have a matcher and it rejects this stat, we return nullopt.
211
2588879
      if (matcher.has_value() && matcher->rejects(name)) {
212
2
        return {};
213
2
      }
214

            
215
2588877
      auto stat = stats_.find(name);
216
2588877
      if (stat != stats_.end()) {
217
132
        return *stat->second;
218
132
      }
219

            
220
2588745
      RefcountPtr<Base> new_stat = histogram_alloc_(joiner, tags, unit);
221
2588745
      stats_.emplace(new_stat->statName(), new_stat);
222
2588745
      return *new_stat;
223
2588877
    }
224

            
225
    OptRef<Base> get(StatName prefix, StatName basename, StatNameTagVectorOptConstRef tags,
226
                     SymbolTable& symbol_table, TextReadout::Type type,
227
688
                     OptRef<const StatsMatcher> matcher = {}) {
228
688
      TagUtility::TagStatNameJoiner joiner(prefix, basename, tags, symbol_table);
229
688
      StatName name = joiner.nameWithTags();
230

            
231
      // If we have a matcher and it rejects this stat, we return nullopt.
232
688
      if (matcher.has_value() && matcher->rejects(name)) {
233
2
        return {};
234
2
      }
235

            
236
686
      auto stat = stats_.find(name);
237
686
      if (stat != stats_.end()) {
238
205
        return *stat->second;
239
205
      }
240

            
241
481
      RefcountPtr<Base> new_stat = text_readout_alloc_(joiner, tags, type);
242
481
      stats_.emplace(new_stat->statName(), new_stat);
243
481
      return *new_stat;
244
686
    }
245

            
246
5729
    std::vector<RefcountPtr<Base>> toVector() const {
247
5729
      std::vector<RefcountPtr<Base>> vec;
248
5729
      vec.reserve(stats_.size());
249
61442
      for (auto& stat : stats_) {
250
61441
        vec.push_back(stat.second);
251
61441
      }
252

            
253
5729
      return vec;
254
5729
    }
255

            
256
176
    bool iterate(const IterateFn<Base>& fn) const {
257
7997
      for (auto& stat : stats_) {
258
7995
        if (!fn(stat.second)) {
259
26
          return false;
260
26
        }
261
7995
      }
262
150
      return true;
263
176
    }
264

            
265
223
    void forEachStat(SizeFn f_size, StatFn<Base> f_stat) const {
266
223
      if (f_size != nullptr) {
267
215
        f_size(stats_.size());
268
215
      }
269
9979
      for (auto const& stat : stats_) {
270
9979
        f_stat(*stat.second);
271
9979
      }
272
223
    }
273

            
274
40
    BaseOptConstRef find(StatName name) const {
275
40
      auto stat = stats_.find(name);
276
40
      if (stat == stats_.end()) {
277
25
        return absl::nullopt;
278
25
      }
279
15
      return std::cref(*stat->second);
280
40
    }
281

            
282
  private:
283
    StatNameHashMap<RefcountPtr<Base>> stats_;
284
    CounterAllocator counter_alloc_;
285
    GaugeAllocator gauge_alloc_;
286
    HistogramAllocator histogram_alloc_;
287
    TextReadoutAllocator text_readout_alloc_;
288
  };
289

            
290
  friend class IsolatedScopeImpl;
291

            
292
  IsolatedStoreImpl(std::unique_ptr<SymbolTable>&& symbol_table);
293

            
294
  SymbolTablePtr symbol_table_storage_;
295
  Allocator alloc_;
296
  IsolatedStatsCache<Counter> counters_;
297
  IsolatedStatsCache<Gauge> gauges_;
298
  IsolatedStatsCache<Histogram> histograms_;
299
  IsolatedStatsCache<TextReadout> text_readouts_;
300
  NullCounterImpl null_counter_;
301
  NullGaugeImpl null_gauge_;
302
  NullHistogramImpl null_histogram_;
303
  NullTextReadoutImpl null_text_readout_;
304

            
305
  // We construct the default-scope lazily to allow subclasses to override
306
  // makeScope(), making it easier to share infrastructure across subclasses
307
  // around managing scopes. Since you can't call an overridden virtual method
308
  // from a parent-class constructor, we instantiate this lazily after the
309
  // object has been fully constructed.
310
  //
311
  // This technically does not need to be declared mutable, but is conceptually
312
  // mutable because we can update this in the const method constRootScope().
313
  mutable ScopeSharedPtr lazy_default_scope_;
314

            
315
  std::vector<ScopeSharedPtr> scopes_;
316
};
317

            
318
class IsolatedScopeImpl : public Scope {
319
public:
320
  IsolatedScopeImpl(const std::string& prefix, IsolatedStoreImpl& store,
321
                    StatsMatcherSharedPtr matcher = nullptr)
322
4
      : prefix_(prefix, store.symbolTable()), store_(store), scope_matcher_(std::move(matcher)) {}
323

            
324
  IsolatedScopeImpl(StatName prefix, IsolatedStoreImpl& store,
325
                    StatsMatcherSharedPtr matcher = nullptr)
326
1870240
      : prefix_(prefix, store.symbolTable()), store_(store), scope_matcher_(std::move(matcher)) {}
327

            
328
1870182
  ~IsolatedScopeImpl() override { prefix_.free(store_.symbolTable()); }
329

            
330
  // Stats::Scope
331
108198479
  SymbolTable& symbolTable() override { return store_.symbolTable(); }
332
177
  const SymbolTable& constSymbolTable() const override { return store_.symbolTable(); }
333
  Counter& counterFromStatNameWithTags(const StatName& name,
334
19228283
                                       StatNameTagVectorOptConstRef tags) override {
335
19228283
    const OptRef<const StatsMatcher> matcher = makeOptRefFromPtr(scope_matcher_.get());
336
19228283
    return store_.counters_.get(prefix(), name, tags, symbolTable(), matcher)
337
19228283
        .value_or(store_.null_counter_);
338
19228283
  }
339
  ScopeSharedPtr createScope(const std::string& name, bool evictable = false,
340
                             const ScopeStatsLimitSettings& limits = {},
341
                             StatsMatcherSharedPtr matcher = nullptr) override;
342
  ScopeSharedPtr scopeFromStatName(StatName name, bool evictable = false,
343
                                   const ScopeStatsLimitSettings& limits = {},
344
                                   StatsMatcherSharedPtr matcher = nullptr) override;
345
  Gauge& gaugeFromStatNameWithTags(const StatName& name, StatNameTagVectorOptConstRef tags,
346
5441880
                                   Gauge::ImportMode import_mode) override {
347
5441880
    const OptRef<const StatsMatcher> matcher = makeOptRefFromPtr(scope_matcher_.get());
348
5441880
    auto gauge = store_.gauges_.get(prefix(), name, tags, symbolTable(), import_mode, matcher);
349
5441880
    if (!gauge.has_value()) {
350
2
      return store_.null_gauge_;
351
2
    }
352
5441878
    gauge->mergeImportMode(import_mode);
353
5441878
    return *gauge;
354
5441880
  }
355
  Histogram& histogramFromStatNameWithTags(const StatName& name, StatNameTagVectorOptConstRef tags,
356
2588879
                                           Histogram::Unit unit) override {
357
2588879
    const OptRef<const StatsMatcher> matcher = makeOptRefFromPtr(scope_matcher_.get());
358
2588879
    return store_.histograms_.get(prefix(), name, tags, symbolTable(), unit, matcher)
359
2588879
        .value_or(store_.null_histogram_);
360
2588879
  }
361
  TextReadout& textReadoutFromStatNameWithTags(const StatName& name,
362
688
                                               StatNameTagVectorOptConstRef tags) override {
363
688
    const OptRef<const StatsMatcher> matcher = makeOptRefFromPtr(scope_matcher_.get());
364
688
    return store_.text_readouts_
365
688
        .get(prefix(), name, tags, symbolTable(), TextReadout::Type::Default, matcher)
366
688
        .value_or(store_.null_text_readout_);
367
688
  }
368
4
  CounterOptConstRef findCounter(StatName name) const override {
369
4
    return store_.counters_.find(name);
370
4
  }
371
32
  GaugeOptConstRef findGauge(StatName name) const override { return store_.gauges_.find(name); }
372
4
  HistogramOptConstRef findHistogram(StatName name) const override {
373
4
    return store_.histograms_.find(name);
374
4
  }
375
  TextReadoutOptConstRef findTextReadout(StatName name) const override {
376
    return store_.text_readouts_.find(name);
377
  }
378

            
379
25
  bool iterate(const IterateFn<Counter>& fn) const override {
380
25
    return store_.counters_.iterate(iterFilter(fn));
381
25
  }
382
105
  bool iterate(const IterateFn<Gauge>& fn) const override {
383
105
    return store_.gauges_.iterate(iterFilter(fn));
384
105
  }
385
23
  bool iterate(const IterateFn<Histogram>& fn) const override {
386
23
    return store_.histograms_.iterate(iterFilter(fn));
387
23
  }
388
23
  bool iterate(const IterateFn<TextReadout>& fn) const override {
389
23
    return store_.text_readouts_.iterate(iterFilter(fn));
390
23
  }
391

            
392
1211093
  Counter& counterFromString(const std::string& name) override {
393
1211093
    StatNameManagedStorage storage(name, symbolTable());
394
1211093
    return counterFromStatName(storage.statName());
395
1211093
  }
396
159506
  Gauge& gaugeFromString(const std::string& name, Gauge::ImportMode import_mode) override {
397
159506
    StatNameManagedStorage storage(name, symbolTable());
398
159506
    return gaugeFromStatName(storage.statName(), import_mode);
399
159506
  }
400
36842
  Histogram& histogramFromString(const std::string& name, Histogram::Unit unit) override {
401
36842
    StatNameManagedStorage storage(name, symbolTable());
402
36842
    return histogramFromStatName(storage.statName(), unit);
403
36842
  }
404
654
  TextReadout& textReadoutFromString(const std::string& name) override {
405
654
    StatNameManagedStorage storage(name, symbolTable());
406
654
    return textReadoutFromStatName(storage.statName());
407
654
  }
408

            
409
54079969
  StatName prefix() const override { return prefix_.statName(); }
410
689
  IsolatedStoreImpl& store() override { return store_; }
411
  const IsolatedStoreImpl& constStore() const override { return store_; }
412

            
413
protected:
414
255206
  void addScopeToStore(const ScopeSharedPtr& scope) { store_.scopes_.push_back(scope); }
415

            
416
private:
417
176
  template <class StatType> IterateFn<StatType> iterFilter(const IterateFn<StatType>& fn) const {
418
    // We determine here what's in the scope by looking at name
419
    // prefixes. Strictly speaking this is not correct, as the same stat can be
420
    // in different scopes, e.g. counter "b.c" in scope "a", and counter "c"
421
    // created in scope "a.b".
422
    //
423
    // There is currently no mechanism to resurrect actual membership of a stat
424
    // in a scope, so we go by name matching. Note that this hack is not needed
425
    // in `ThreadLocalStore`, which has accurate maps describing which stats are
426
    // in which scopes.
427
    //
428
    // TODO(jmarantz): In the scope of this limited implementation, it would be
429
    // faster to match on the StatName prefix. This would be possible if
430
    // SymbolTable exposed a split() method.
431
176
    std::string prefix_str = constSymbolTable().toString(prefix_.statName());
432
176
    if (!prefix_str.empty() && !absl::EndsWith(prefix_str, ".")) {
433
120
      prefix_str += ".";
434
120
    }
435
7997
    return [fn, prefix_str](const RefcountPtr<StatType>& stat) -> bool {
436
7995
      return !absl::StartsWith(stat->name(), prefix_str) || fn(stat);
437
7995
    };
438
176
  }
439

            
440
  StatNameStorage prefix_;
441
  IsolatedStoreImpl& store_;
442
  StatsMatcherSharedPtr scope_matcher_;
443
};
444

            
445
} // namespace Stats
446
} // namespace Envoy