blob: ae96c1dbbacaf40d175ff87ca907321120d30676 [file]
/** @file
The implementations of the Metrics::Counter API class.
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "tsutil/Assert.h"
#include <memory>
#include <mutex>
#include <optional>
#include <variant>
#include <vector>
#include "tsutil/Metrics.h"
namespace ts
{
Metrics &
Metrics::instance()
{
// This is the singleton instance of the metrics storage class.
static std::shared_ptr<Storage> _metrics_store = std::make_shared<Storage>();
thread_local Metrics _instance(_metrics_store);
return _instance;
}
void
Metrics::Storage::addBlob() // The mutex must be held before calling this!
{
auto blob = std::make_unique<Metrics::NamesAndAtomics>();
debug_assert(blob);
debug_assert(_cur_blob < MAX_BLOBS);
_blobs[++_cur_blob] = std::move(blob);
_cur_off = 0;
}
Metrics::IdType
Metrics::Storage::create(std::string_view name, const MetricType type)
{
std::lock_guard lock(_mutex);
auto it = _lookups.find(name);
if (it != _lookups.end()) {
return it->second;
}
Metrics::IdType id = _makeId(_cur_blob, _cur_off, type);
Metrics::NamesAndAtomics *blob = _blobs[_cur_blob].get();
Metrics::NameStorage &names = std::get<0>(*blob);
names[_cur_off] = std::make_tuple(std::string(name), id);
_lookups.emplace(std::get<0>(names[_cur_off]), id);
if (++_cur_off >= MAX_SIZE) {
addBlob(); // This resets _cur_off to 0 as well
}
return id;
}
Metrics::IdType
Metrics::Storage::lookup(const std::string_view name) const
{
std::lock_guard lock(_mutex);
auto it = _lookups.find(name);
if (it != _lookups.end()) {
return it->second;
}
return NOT_FOUND;
}
Metrics::AtomicType *
Metrics::Storage::lookup(Metrics::IdType id, std::string_view *out_name, Metrics::MetricType *out_type) const
{
auto [blob_ix, offset] = _splitID(id);
Metrics::NamesAndAtomics *blob = _blobs[blob_ix].get();
// Do a sanity check on the ID, to make sure we don't index outside of the realm of possibility.
if (!blob || (blob_ix == _cur_blob && offset > _cur_off)) {
blob = _blobs[0].get();
offset = 0;
}
if (out_name) {
*out_name = std::get<0>(std::get<0>(*blob)[offset]);
}
if (out_type) {
// don't trust the passed in id to get the type as it might have been manufactured (i.e. from iterators)
// so get the type from the storage tuple.
*out_type = _extractType(std::get<1>(std::get<0>(*blob)[offset]));
}
return &((std::get<1>(*blob)[offset]));
}
Metrics::AtomicType *
Metrics::Storage::lookup(const std::string_view name, Metrics::IdType *out_id, Metrics::MetricType *out_type) const
{
Metrics::IdType id = lookup(name);
Metrics::AtomicType *result = nullptr;
if (id != NOT_FOUND) {
result = lookup(id);
}
if (nullptr != out_id) {
*out_id = id;
}
if (out_type && id != NOT_FOUND) {
*out_type = _extractType(id);
}
return result;
}
std::string_view
Metrics::Storage::name(Metrics::IdType id) const
{
auto [blob_ix, offset] = _splitID(id);
Metrics::NamesAndAtomics *blob = _blobs[blob_ix].get();
// Do a sanity check on the ID, to make sure we don't index outside of the realm of possibility.
if (!blob || (blob_ix == _cur_blob && offset > _cur_off)) {
blob = _blobs[0].get();
offset = 0;
}
const std::string &result = std::get<0>(std::get<0>(*blob)[offset]);
return result;
}
Metrics::MetricType
Metrics::Storage::type(IdType id) const
{
return _extractType(id);
}
Metrics::SpanType
Metrics::Storage::createSpan(size_t size, Metrics::MetricType type, Metrics::IdType *id)
{
release_assert(size <= MAX_SIZE);
std::lock_guard lock(_mutex);
if (_cur_off + size > MAX_SIZE) {
addBlob();
}
Metrics::IdType span_start = _makeId(_cur_blob, _cur_off, type);
Metrics::NamesAndAtomics *blob = _blobs[_cur_blob].get();
Metrics::AtomicStorage &atomics = std::get<1>(*blob);
Metrics::SpanType span = Metrics::SpanType(&atomics[_cur_off], size);
if (id) {
*id = span_start;
}
_cur_off += size;
return span;
}
bool
Metrics::Storage::rename(Metrics::IdType id, std::string_view name)
{
auto [blob_ix, offset] = _splitID(id);
Metrics::NamesAndAtomics *blob = _blobs[blob_ix].get();
// We can only rename Metrics that are already allocated
if (!blob || (blob_ix == _cur_blob && offset > _cur_off)) {
return false;
}
std::string &cur = std::get<0>(std::get<0>(*blob)[offset]);
std::lock_guard lock(_mutex);
if (cur.length() > 0) {
_lookups.erase(cur);
}
cur = name;
_lookups.emplace(cur, id);
return true;
}
// Iterator implementation
void
Metrics::iterator::next()
{
auto [blob, offset] = _metrics._splitID(_it);
if (++offset == MAX_SIZE) {
++blob;
offset = 0;
}
_it = _makeId(blob, offset, MetricType::COUNTER);
}
namespace details
{
struct DerivedMetric {
Metrics::IdType metric;
std::vector<Metrics::AtomicType *> derived_from;
};
struct DerivativeMetrics {
std::vector<DerivedMetric> metrics;
std::mutex metrics_lock;
void
update()
{
auto &instance = Metrics::instance();
std::lock_guard l(metrics_lock);
for (auto &m : metrics) {
int64_t sum = 0;
for (auto d : m.derived_from) {
sum += d->load();
}
instance[m.metric].store(sum);
}
}
void
push_back(const DerivedMetric &m)
{
std::lock_guard l(metrics_lock);
metrics.push_back(std::move(m));
}
static DerivativeMetrics &
instance()
{
static DerivativeMetrics theDerivedMetrics;
return theDerivedMetrics;
}
};
} // namespace details
void
Metrics::Derived::derive(const std::initializer_list<Metrics::Derived::DerivedMetricSpec> &metrics)
{
auto &instance = Metrics::instance();
for (auto &m : metrics) {
details::DerivedMetric dm{};
dm.metric = instance._create(m.derived_name, m.derived_type);
for (auto &d : m.derived_from) {
if (std::holds_alternative<Metrics::AtomicType *>(d)) {
dm.derived_from.push_back(std::get<Metrics::AtomicType *>(d));
} else if (std::holds_alternative<Metrics::IdType>(d)) {
dm.derived_from.push_back(instance.lookup(std::get<Metrics::IdType>(d)));
} else if (std::holds_alternative<std::string_view>(d)) {
dm.derived_from.push_back(instance.lookup(instance.lookup(std::get<std::string_view>(d))));
}
}
details::DerivativeMetrics::instance().push_back(dm);
}
}
void
Metrics::Derived::update_derived()
{
details::DerivativeMetrics::instance().update();
}
Metrics::StaticString &
Metrics::StaticString::instance()
{
static Metrics::StaticString i{};
return i;
}
void
Metrics::StaticString::_createString(const std::string &name, const std::string_view value)
{
std::lock_guard lock(_mutex);
_strings[name] = value;
}
std::optional<std::string_view>
Metrics::StaticString::lookup(const std::string &name) const
{
std::lock_guard lock(_mutex);
auto it = _strings.find(name);
std::optional<std::string_view> result{};
if (it != _strings.end()) {
result = it->second;
}
return result;
}
} // namespace ts