Skip to content

Commit eb61401

Browse files
net: Add bytes sent/received metrics
Adds two basic metrics that track bytes sent and received from the networking stack. This can give a better per application insight than what host wide metrics (for example via node exporter) can do. Further it can serve as a comparison point with host based metrics for possible write amplification (though this is less likely on the network side). The patch follows a similar pattern as to how memory and disk based metrics work. Note one might be inclined to introduce some grouping into the metrics in relation to either the interface or source IP similar to how we have mountpoints and/or groups on the disk side. While this sounds useful at first in practice it would be less useful. Often for the major cloud providers and similar in self hosted environments there is only a single interface/source-IP and routing happens at a later point (switches, routers etc.). Further, adding this separation would make the implementation more expensive in either compute or memory space. We link the metrics to the cpu scheduling group which allows for getting a more detailed picture of where the network usage is coming from. Further it sets up for future network scheduling at the group level.
1 parent 808766d commit eb61401

File tree

7 files changed

+117
-1
lines changed

7 files changed

+117
-1
lines changed

include/seastar/net/api.hh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,28 @@ public:
444444
bool supports_ipv6() const;
445445
};
446446

447+
class statistics {
448+
uint64_t _bytes_sent;
449+
uint64_t _bytes_received;
450+
public:
451+
statistics(uint64_t bytes_sent, uint64_t bytes_received)
452+
: _bytes_sent(bytes_sent), _bytes_received(bytes_received) {}
453+
uint64_t bytes_sent() const {
454+
return _bytes_sent;
455+
};
456+
uint64_t bytes_received() const {
457+
return _bytes_received;
458+
};
459+
};
460+
461+
namespace metrics {
462+
class metric_groups;
463+
class label_instance;
464+
}
465+
466+
void register_net_metrics_for_scheduling_group(
467+
metrics::metric_groups& m, unsigned sg_id, const metrics::label_instance& name);
468+
447469
class network_stack {
448470
public:
449471
virtual ~network_stack() {}
@@ -468,6 +490,11 @@ public:
468490
return false;
469491
}
470492

493+
// Return network stats (bytes sent/received etc.) for this stack and scheduling group
494+
virtual statistics stats(unsigned scheduling_group_id) = 0;
495+
// Clears the stats for this stack and scheduling group
496+
virtual void clear_stats(unsigned scheduling_group_id) = 0;
497+
471498
/**
472499
* Returns available network interfaces. This represents a
473500
* snapshot of interfaces available at call time, hence the

include/seastar/net/posix-stack.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ public:
215215
virtual bool has_per_core_namespace() override { return _reuseport; };
216216
bool supports_ipv6() const override;
217217
std::vector<network_interface> network_interfaces() override;
218+
virtual statistics stats(unsigned scheduling_group_id) override;
219+
virtual void clear_stats(unsigned scheduling_group_id) override;
218220
};
219221

220222
class posix_ap_network_stack : public posix_network_stack {

src/core/reactor.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,9 @@ reactor::task_queue::register_stats() {
960960
}, sm::description("Total amount in milliseconds we were in violation of the task quota"),
961961
{group_label}),
962962
});
963+
964+
register_net_metrics_for_scheduling_group(new_metrics, _id, group_label);
965+
963966
_metrics = std::exchange(new_metrics, {});
964967
}
965968

@@ -2560,7 +2563,6 @@ void reactor::register_metrics() {
25602563
sm::make_counter("abandoned_failed_futures", _abandoned_failed_futures, sm::description("Total number of abandoned failed futures, futures destroyed while still containing an exception")),
25612564
});
25622565

2563-
namespace sm = seastar::metrics;
25642566
_metric_groups.add_group("reactor", {
25652567
sm::make_counter("fstream_reads", _io_stats.fstream_reads,
25662568
sm::description(

src/net/native-stack-impl.hh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ namespace seastar {
2929

3030
extern logger seastar_logger;
3131

32+
namespace internal {
33+
34+
struct native_stack_net_stats {
35+
#ifdef SEASTAR_BUILD_SHARED_LIBS
36+
static thread_local std::array<unsigned, max_scheduling_groups()> bytes_sent;
37+
static thread_local std::array<unsigned, max_scheduling_groups()> bytes_received;
38+
#else
39+
static inline thread_local std::array<unsigned, max_scheduling_groups()> bytes_sent = {};
40+
static inline thread_local std::array<unsigned, max_scheduling_groups()> bytes_received = {};
41+
#endif
42+
};
43+
44+
}
45+
3246
namespace net {
3347

3448
using namespace seastar;
@@ -172,6 +186,8 @@ public:
172186
}
173187
return _conn->wait_for_data().then([this] {
174188
_buf = _conn->read();
189+
auto sg_id = internal::scheduling_group_index(current_scheduling_group());
190+
internal::native_stack_net_stats::bytes_received[sg_id] += _buf.len();
175191
_cur_frag = 0;
176192
_eof = !_buf.len();
177193
return get();
@@ -193,6 +209,8 @@ public:
193209
: _conn(std::move(conn)) {}
194210
using data_sink_impl::put;
195211
virtual future<> put(packet p) override {
212+
auto sg_id = internal::scheduling_group_index(current_scheduling_group());
213+
internal::native_stack_net_stats::bytes_sent[sg_id] += p.len();
196214
return _conn->send(std::move(p));
197215
}
198216
virtual future<> close() override {

src/net/native-stack.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,18 @@ class native_network_stack : public network_stack {
194194
friend class native_network_interface;
195195

196196
std::vector<network_interface> network_interfaces() override;
197+
198+
virtual statistics stats(unsigned scheduling_group_id) override {
199+
return statistics{
200+
internal::native_stack_net_stats::bytes_sent[scheduling_group_id],
201+
internal::native_stack_net_stats::bytes_received[scheduling_group_id],
202+
};
203+
}
204+
205+
virtual void clear_stats(unsigned scheduling_group_id) override {
206+
internal::native_stack_net_stats::bytes_sent[scheduling_group_id] = 0;
207+
internal::native_stack_net_stats::bytes_received[scheduling_group_id] = 0;
208+
}
197209
};
198210

199211
thread_local promise<std::unique_ptr<network_stack>> native_network_stack::ready_promise;
@@ -427,6 +439,15 @@ std::vector<network_interface> native_network_stack::network_interfaces() {
427439
return res;
428440
}
429441

442+
} // namespace net
443+
444+
namespace internal {
445+
446+
#ifdef SEASTAR_BUILD_SHARED_LIBS
447+
thread_local std::array<unsigned, max_scheduling_groups()> native_stack_net_stats::bytes_sent = {};
448+
thread_local std::array<unsigned, max_scheduling_groups()> native_stack_net_stats::bytes_received = {};
449+
#endif
450+
430451
}
431452

432453
}

src/net/posix-stack.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ copy_reinterpret_cast(const void* ptr) {
8181
return tmp;
8282
}
8383

84+
thread_local std::array<unsigned, seastar::max_scheduling_groups()> bytes_sent = {};
85+
thread_local std::array<unsigned, seastar::max_scheduling_groups()> bytes_received = {};
86+
8487
}
8588

8689
namespace seastar {
@@ -637,6 +640,8 @@ posix_data_source_impl::get() {
637640
_config.buffer_size /= 2;
638641
_config.buffer_size = std::max(_config.buffer_size, _config.min_buffer_size);
639642
}
643+
auto sg_id = internal::scheduling_group_index(current_scheduling_group());
644+
bytes_received[sg_id] += b.size();
640645
return b;
641646
});
642647
}
@@ -671,12 +676,16 @@ std::vector<iovec> to_iovec(std::vector<temporary_buffer<char>>& buf_vec) {
671676

672677
future<>
673678
posix_data_sink_impl::put(temporary_buffer<char> buf) {
679+
auto sg_id = internal::scheduling_group_index(current_scheduling_group());
680+
bytes_sent[sg_id] += buf.size();
674681
return _fd.write_all(buf.get(), buf.size()).then([d = buf.release()] {});
675682
}
676683

677684
future<>
678685
posix_data_sink_impl::put(packet p) {
679686
_p = std::move(p);
687+
auto sg_id = internal::scheduling_group_index(current_scheduling_group());
688+
bytes_sent[sg_id] += _p.len();
680689
return _fd.write_all(_p).then([this] { _p.reset(); });
681690
}
682691

@@ -876,13 +885,17 @@ future<> posix_datagram_channel::send(const socket_address& dst, const char *mes
876885
auto len = strlen(message);
877886
auto a = dst;
878887
resolve_outgoing_address(a);
888+
auto sg_id = internal::scheduling_group_index(current_scheduling_group());
889+
bytes_sent[sg_id] += len;
879890
return _fd.sendto(a, message, len)
880891
.then([len] (size_t size) { assert(size == len); });
881892
}
882893

883894
future<> posix_datagram_channel::send(const socket_address& dst, packet p) {
884895
auto len = p.len();
885896
_send.prepare(dst, std::move(p));
897+
auto sg_id = internal::scheduling_group_index(current_scheduling_group());
898+
bytes_sent[sg_id] += len;
886899
return _fd.sendmsg(&_send._hdr)
887900
.then([len] (size_t size) { assert(size == len); });
888901
}
@@ -954,6 +967,8 @@ posix_datagram_channel::receive() {
954967
break;
955968
}
956969
}
970+
auto sg_id = internal::scheduling_group_index(current_scheduling_group());
971+
bytes_received[sg_id] += size;
957972
return make_ready_future<datagram>(datagram(std::make_unique<posix_datagram>(
958973
_recv._src_addr, dst ? *dst : _address, packet(fragment{_recv._buffer, size}, make_deleter([buf = _recv._buffer] { delete[] buf; })))));
959974
}).handle_exception([p = _recv._buffer](auto ep) {
@@ -1199,6 +1214,18 @@ std::vector<network_interface> posix_network_stack::network_interfaces() {
11991214
return std::vector<network_interface>(thread_local_interfaces.begin(), thread_local_interfaces.end());
12001215
}
12011216

1217+
statistics posix_network_stack::stats(unsigned scheduling_group_id) {
1218+
return statistics{
1219+
bytes_sent[scheduling_group_id],
1220+
bytes_received[scheduling_group_id],
1221+
};
1222+
}
1223+
1224+
void posix_network_stack::clear_stats(unsigned scheduling_group_id) {
1225+
bytes_sent[scheduling_group_id] = 0;
1226+
bytes_received[scheduling_group_id] = 0;
1227+
}
1228+
12021229
}
12031230

12041231
}

src/net/stack.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ module;
3131
#ifdef SEASTAR_MODULE
3232
module seastar;
3333
#else
34+
#include <seastar/core/metrics_api.hh>
35+
#include <seastar/core/reactor.hh>
3436
#include <seastar/net/stack.hh>
3537
#include <seastar/net/inet_address.hh>
3638
#endif
@@ -286,4 +288,21 @@ std::vector<network_interface> network_stack::network_interfaces() {
286288
return {};
287289
}
288290

291+
void register_net_metrics_for_scheduling_group(
292+
metrics::metric_groups &metrics, unsigned sg_id, const metrics::label_instance& name) {
293+
namespace sm = seastar::metrics;
294+
metrics.add_group("network", {
295+
sm::make_counter("bytes_sent", [sg_id] { return engine().net().stats(sg_id).bytes_sent(); },
296+
sm::description("Counts the number of bytes written to network sockets."), {name}),
297+
sm::make_counter("bytes_received", [sg_id] { return engine().net().stats(sg_id).bytes_received(); },
298+
sm::description("Counts the number of bytes received from network sockets."), {name}),
299+
});
300+
301+
// need to clear stats in case we recreated a SG with the same id
302+
// but avoid during reactor startup
303+
if (engine_is_ready()) {
304+
engine().net().clear_stats(sg_id);
305+
}
306+
}
307+
289308
}

0 commit comments

Comments
 (0)