149 lines
5.1 KiB
C++
149 lines
5.1 KiB
C++
/*
|
|
* MetricClient.actor.cpp
|
|
*
|
|
* This source file is part of the FoundationDB open source project
|
|
*
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
|
*
|
|
* Licensed 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 "fdbserver/MetricClient.h"
|
|
#include "fdbrpc/Stats.h"
|
|
#include "flow/FastRef.h"
|
|
#include "flow/IRandom.h"
|
|
#include "flow/Knobs.h"
|
|
#include "flow/OTELMetrics.h"
|
|
#include "flow/TDMetric.actor.h"
|
|
#include "flow/Msgpack.h"
|
|
#include "flow/Trace.h"
|
|
#include "flow/flow.h"
|
|
#include <cstddef>
|
|
#ifndef WIN32
|
|
#include <sys/socket.h>
|
|
#endif
|
|
#include "flow/network.h"
|
|
#include "flow/IUDPSocket.h"
|
|
#include "flow/IConnection.h"
|
|
#include "flow/actorcompiler.h"
|
|
|
|
UDPMetricClient::UDPMetricClient()
|
|
: socket_fd(-1), model(knobToMetricModel(FLOW_KNOBS->METRICS_DATA_MODEL)),
|
|
buf{ MsgpackBuffer{ .buffer = std::make_unique<uint8_t[]>(1024), .data_size = 0, .buffer_size = 1024 } },
|
|
address((model == STATSD) ? FLOW_KNOBS->STATSD_UDP_EMISSION_ADDR : FLOW_KNOBS->OTEL_UDP_EMISSION_ADDR),
|
|
port((model == STATSD) ? FLOW_KNOBS->STATSD_UDP_EMISSION_PORT : FLOW_KNOBS->OTEL_UDP_EMISSION_PORT) {
|
|
NetworkAddress destAddress = NetworkAddress::parse(address + ":" + std::to_string(port));
|
|
socket = INetworkConnections::net()->createUDPSocket(destAddress);
|
|
model = knobToMetricModel(FLOW_KNOBS->METRICS_DATA_MODEL);
|
|
}
|
|
|
|
// Since MSG_DONTWAIT isn't defined for Windows, we need to add a
|
|
// ifndef guard here to avoid any compilation issues
|
|
void UDPMetricClient::send_packet(int fd, const void* data, size_t len) {
|
|
#ifndef WIN32
|
|
::send(fd, data, len, MSG_DONTWAIT);
|
|
#endif
|
|
}
|
|
|
|
void UDPMetricClient::send(MetricCollection* metrics) {
|
|
if (!socket.isReady()) {
|
|
return;
|
|
}
|
|
socket_fd = socket.get()->native_handle();
|
|
if (socket_fd == -1)
|
|
return;
|
|
if (model == OTLP) {
|
|
std::vector<std::vector<OTEL::OTELSum>> sums;
|
|
std::vector<OTEL::OTELGauge> gauges;
|
|
|
|
// Define custom serialize functions
|
|
auto f_sums = [](const std::vector<OTEL::OTELSum>& vec, MsgpackBuffer& buf) {
|
|
typedef void (*func_ptr)(const OTEL::OTELSum&, MsgpackBuffer&);
|
|
func_ptr f = OTEL::serialize;
|
|
serialize_vector(vec, buf, f);
|
|
};
|
|
|
|
auto f_hists = [](const std::vector<OTEL::OTELHistogram>& vec, MsgpackBuffer& buf) {
|
|
typedef void (*func_ptr)(const OTEL::OTELHistogram&, MsgpackBuffer&);
|
|
func_ptr f = OTEL::serialize;
|
|
serialize_vector(vec, buf, f);
|
|
};
|
|
|
|
auto f_gauge = [](const std::vector<OTEL::OTELGauge>& vec, MsgpackBuffer& buf) {
|
|
typedef void (*func_ptr)(const OTEL::OTELGauge&, MsgpackBuffer&);
|
|
func_ptr f = OTEL::serialize;
|
|
serialize_vector(vec, buf, f);
|
|
};
|
|
|
|
std::vector<OTEL::OTELSum> currentSums;
|
|
size_t current_msgpack = 0;
|
|
for (const auto& [_, s] : metrics->sumMap) {
|
|
if (current_msgpack < MAX_OTELSUM_PACKET_SIZE) {
|
|
currentSums.push_back(std::move(s));
|
|
current_msgpack += s.getMsgpackBytes();
|
|
} else {
|
|
sums.push_back(std::move(currentSums));
|
|
currentSums.clear();
|
|
current_msgpack = 0;
|
|
}
|
|
}
|
|
if (!sums.empty()) {
|
|
for (const auto& currSums : sums) {
|
|
serialize_ext(currSums, buf, OTEL::OTELMetricType::Sum, f_sums);
|
|
send_packet(socket_fd, buf.buffer.get(), buf.data_size);
|
|
int error = errno;
|
|
TraceEvent("MetricsSumUdpErrno", UID()).detail("Errno", error);
|
|
buf.reset();
|
|
}
|
|
metrics->sumMap.clear();
|
|
}
|
|
|
|
// Each histogram should be in a seperate because of their large sizes
|
|
// Expected DDSketch size is ~4200 entries * 9 bytes = 37800
|
|
for (const auto& [_, h] : metrics->histMap) {
|
|
const std::vector<OTEL::OTELHistogram> singleHist{ std::move(h) };
|
|
serialize_ext(singleHist, buf, OTEL::OTELMetricType::Hist, f_hists);
|
|
send_packet(socket_fd, buf.buffer.get(), buf.data_size);
|
|
int error = errno;
|
|
TraceEvent("MetricsHistUdpErrno", UID()).detail("Errno", error);
|
|
buf.reset();
|
|
}
|
|
|
|
metrics->histMap.clear();
|
|
|
|
for (const auto& [_, g] : metrics->gaugeMap) {
|
|
gauges.push_back(std::move(g));
|
|
}
|
|
if (!gauges.empty()) {
|
|
serialize_ext(gauges, buf, OTEL::OTELMetricType::Gauge, f_gauge);
|
|
send_packet(socket_fd, buf.buffer.get(), buf.data_size);
|
|
int error = errno;
|
|
TraceEvent("MetricsGaugeUdpErrno", UID()).detail("Errno", error);
|
|
metrics->gaugeMap.clear();
|
|
buf.reset();
|
|
}
|
|
} else if (model == MetricsDataModel::STATSD) {
|
|
std::string messages;
|
|
for (const auto& msg : metrics->statsd_message) {
|
|
// Account for max udp packet size (+1 since we add '\n')
|
|
if (messages.size() + msg.size() + 1 < IUDPSocket::MAX_PACKET_SIZE) {
|
|
messages += (std::move(msg) + '\n');
|
|
} else {
|
|
send_packet(socket_fd, buf.buffer.get(), buf.data_size);
|
|
}
|
|
}
|
|
if (!messages.empty()) {
|
|
send_packet(socket_fd, messages.data(), messages.size());
|
|
}
|
|
metrics->statsd_message.clear();
|
|
}
|
|
} |