Repository: envoyproxy/envoy
File: source/common/stats/thread_local_store.cc (C++)

Code before:
    }

    tls_cache_->runOnAllThreads(
        [histograms](OptRef<TlsCache> tls_cache) { tls_cache->eraseHistograms(*histograms); });
  }
}

ThreadLocalStoreImpl::ScopeImpl::ScopeImpl(ThreadLocalStoreImpl& parent, StatName prefix,
                                           bool evictable, const ScopeStatsLimitSettings& limits)
    : scope_id_(parent.next_scope_id_++), parent_(parent), evictable_(evictable), limits_(limits),
      prefix_(prefix, parent.alloc_.symbolTable()),
      central_cache_(new CentralCacheEntry(parent.alloc_.symbolTable())) {
  Thread::LockGuard lock(parent_.lock_);
  if (limits_.max_counters.has_value() && parent_.counters_overflow_ == nullptr) {
    StatNamePool pool(parent_.symbolTable());
    StatName name = pool.add("stats.overflow.counters");
    parent_.counters_overflow_ = parent_.alloc_.makeCounter(name, name, {});
  }
  if (limits_.max_gauges.has_value() && parent_.gauges_overflow_ == nullptr) {
    StatNamePool pool(parent_.symbolTable());
    StatName name = pool.add("stats.overflow.gauges");
    parent_.gauges_overflow_ = parent_.alloc_.makeCounter(name, name, {});
  }
  if (limits_.max_histograms.has_value() && parent_.histograms_overflow_ == nullptr) {
    StatNamePool pool(parent_.symbolTable());
    StatName name = pool.add("stats.overflow.histograms");
    parent_.histograms_overflow_ = parent_.alloc_.makeCounter(name, name, {});
  }
}

ThreadLocalStoreImpl::ScopeImpl::~ScopeImpl() {
  // Helps reproduce a previous race condition by pausing here in tests while we
  // loop over scopes. 'this' will not have been removed from the scopes_ table
  // yet, so we need to be careful.
  parent_.sync_.syncPoint(DeleteScopeSync);

  // Note that scope iteration is thread-safe due to the lock held in
  // releaseScopeCrossThread. For more details see the comment in
  // `ThreadLocalStoreImpl::iterHelper`, and the lock it takes prior to the loop.
  parent_.releaseScopeCrossThread(this);
  prefix_.free(symbolTable());
}

// Helper for managing the potential truncation of tags from the metric names and
// converting them to StatName. Making the tag extraction optional within this class simplifies the
// RAII ergonomics (as opposed to making the construction of this object conditional).
//
// The StatNameTagVector returned by this object will be valid as long as this object is in scope
// and the provided stat_name_tags are valid.
//
// When tag extraction is not done, this class is just a passthrough for the provided name/tags.

Review comment (bug):
These do not seem to match OTLP at all, so maybe keep it simple? E.g. create a `server.stats_overflow` counter, and then document it in the main page? It would be incremented every time a measurement cannot be recorded because of the scope cardinality overflow

Code after:
    }

    tls_cache_->runOnAllThreads(
        [histograms](OptRef<TlsCache> tls_cache) { tls_cache->eraseHistograms(*histograms); });
  }
}

ThreadLocalStoreImpl::ScopeImpl::ScopeImpl(ThreadLocalStoreImpl& parent, StatName prefix,
                                           bool evictable, const ScopeStatsLimitSettings& limits)
    : scope_id_(parent.next_scope_id_++), parent_(parent), evictable_(evictable), limits_(limits),
      prefix_(prefix, parent.alloc_.symbolTable()),
      central_cache_(new CentralCacheEntry(parent.alloc_.symbolTable())) {
  parent_.ensureOverflowStats(limits_);
}

ThreadLocalStoreImpl::ScopeImpl::~ScopeImpl() {
  // Helps reproduce a previous race condition by pausing here in tests while we
  // loop over scopes. 'this' will not have been removed from the scopes_ table
  // yet, so we need to be careful.
  parent_.sync_.syncPoint(DeleteScopeSync);

  // Note that scope iteration is thread-safe due to the lock held in
  // releaseScopeCrossThread. For more details see the comment in
  // `ThreadLocalStoreImpl::iterHelper`, and the lock it takes prior to the loop.
  parent_.releaseScopeCrossThread(this);
  prefix_.free(symbolTable());
}

// Helper for managing the potential truncation of tags from the metric names and
// converting them to StatName. Making the tag extraction optional within this class simplifies the
// RAII ergonomics (as opposed to making the construction of this object conditional).
//
// The StatNameTagVector returned by this object will be valid as long as this object is in scope
// and the provided stat_name_tags are valid.
//
// When tag extraction is not done, this class is just a passthrough for the provided name/tags.
class StatNameTagHelper {
public:
  StatNameTagHelper(ThreadLocalStoreImpl& tls, StatName name,
                    const absl::optional<StatNameTagVector>& stat_name_tags)
      : pool_(tls.symbolTable()), stat_name_tags_(stat_name_tags.value_or(StatNameTagVector())) {
    if (!stat_name_tags) {
      TagVector tags;
      tag_extracted_name_ =
          pool_.add(tls.tagProducer().produceTags(tls.symbolTable().toString(name), tags));
      StatName empty;
      for (const auto& tag : tags) {
        StatName tag_name = tls.wellKnownTags().getBuiltin(tag.name_, empty);
        if (tag_name.empty()) {
          tag_name = pool_.add(tag.name_);
        }