Skip to content

Commit d59f0ff

Browse files
committed
Add session metrics logging for statesync server
Added per-request logging of disk I/O statistics, upsert counts, and throughput metrics to enable performance monitoring and debugging of statesync server operations. Fix the read counter in async io to use 64 bit, 32 bit was prone to overflow. Add counter for number of bytes read in async io. This code was generated using Claude Sonnet 4.5
1 parent 034e382 commit d59f0ff

File tree

4 files changed

+100
-20
lines changed

4 files changed

+100
-20
lines changed

category/async/io.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,12 +302,13 @@ AsyncIO::~AsyncIO()
302302
::close(fds_.msgwrite);
303303
}
304304

305-
void AsyncIO::account_read_()
305+
void AsyncIO::account_read_(size_t size)
306306
{
307307
if (++records_.inflight_rd > records_.max_inflight_rd) {
308308
records_.max_inflight_rd = records_.inflight_rd;
309309
}
310310
++records_.nreads;
311+
records_.bytes_read += size;
311312
}
312313

313314
void AsyncIO::submit_request_sqe_(
@@ -520,7 +521,7 @@ size_t AsyncIO::poll_uring_(bool blocking, unsigned poll_rings_mask)
520521
auto const &read = concurrent_read_ios_pending_.front();
521522
submit_request_sqe_(
522523
read.buffer, read.offset, read.op, read.op->io_priority());
523-
account_read_();
524+
account_read_(read.buffer.size());
524525
concurrent_read_ios_pending_.pop_front();
525526
}
526527
}

category/async/io.hpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ struct IORecord
5353
unsigned max_inflight_rd_scatter{0};
5454
unsigned max_inflight_wr{0};
5555

56-
unsigned nreads{0};
56+
uint64_t nreads{0};
5757
// Reads and scatter reads which got a EAGAIN and were retried
5858
unsigned reads_retried{0};
59+
uint64_t bytes_read{0};
5960
};
6061

6162
class AsyncIO final
@@ -129,7 +130,7 @@ class AsyncIO final
129130
void *uring_data, enum erased_connected_operation::io_priority prio);
130131
void submit_request_(timed_invocation_state *state, void *uring_data);
131132

132-
void account_read_();
133+
void account_read_(size_t size);
133134

134135
void poll_uring_while_submission_queue_full_();
135136
size_t poll_uring_(bool blocking, unsigned poll_rings_mask);
@@ -235,6 +236,16 @@ class AsyncIO final
235236
return records_.inflight_ts.load(std::memory_order_relaxed);
236237
}
237238

239+
uint64_t total_reads_submitted() const noexcept
240+
{
241+
return records_.nreads;
242+
}
243+
244+
uint64_t total_bytes_read() const noexcept
245+
{
246+
return records_.bytes_read;
247+
}
248+
238249
unsigned concurrent_read_io_limit() const noexcept
239250
{
240251
return concurrent_read_io_limit_;
@@ -338,6 +349,8 @@ class AsyncIO final
338349
records_.max_inflight_rd_scatter = 0;
339350
records_.max_inflight_wr = 0;
340351
records_.nreads = 0;
352+
records_.bytes_read = 0;
353+
records_.reads_retried = 0;
341354
}
342355

343356
size_t submit_read_request(
@@ -355,8 +368,9 @@ class AsyncIO final
355368
return size_t(-1); // we never complete immediately
356369
}
357370

371+
auto const size = buffer.size();
358372
submit_request_(buffer, offset, uring_data, uring_data->io_priority());
359-
account_read_();
373+
account_read_(size);
360374
return size_t(-1); // we never complete immediately
361375
}
362376

@@ -659,7 +673,7 @@ class AsyncIO final
659673
using erased_connected_operation_ptr =
660674
AsyncIO::erased_connected_operation_unique_ptr_type;
661675

662-
static_assert(sizeof(AsyncIO) == 272);
676+
static_assert(sizeof(AsyncIO) == 296);
663677
static_assert(alignof(AsyncIO) == 8);
664678

665679
namespace detail

category/statesync/statesync_server.cpp

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// You should have received a copy of the GNU General Public License
1414
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1515

16+
#include <category/async/io.hpp>
1617
#include <category/core/assert.h>
1718
#include <category/core/basic_formatter.hpp>
1819
#include <category/core/byte_string.hpp>
@@ -69,7 +70,8 @@ byte_string from_prefix(uint64_t const prefix, size_t const n_bytes)
6970

7071
bool send_deletion(
7172
monad_statesync_server *const sync, monad_sync_request const &rq,
72-
monad_statesync_server_context &ctx)
73+
monad_statesync_server_context &ctx, uint64_t *const num_upserts,
74+
uint64_t *const upsert_bytes)
7375
{
7476
MONAD_ASSERT(
7577
rq.old_target <= rq.target || rq.old_target == INVALID_BLOCK_NUM);
@@ -78,8 +80,10 @@ bool send_deletion(
7880
return true;
7981
}
8082

81-
auto const fn = [sync, prefix = from_prefix(rq.prefix, rq.prefix_bytes)](
82-
Deletion const &deletion) {
83+
auto const fn = [sync,
84+
prefix = from_prefix(rq.prefix, rq.prefix_bytes),
85+
num_upserts,
86+
upsert_bytes](Deletion const &deletion) {
8387
auto const &[addr, key] = deletion;
8488
auto const hash = keccak256(addr.bytes);
8589
byte_string_view const view{hash.bytes, sizeof(hash.bytes)};
@@ -94,6 +98,8 @@ bool send_deletion(
9498
sizeof(addr),
9599
nullptr,
96100
0);
101+
++(*num_upserts);
102+
*upsert_bytes += sizeof(addr);
97103
}
98104
else {
99105
auto const skey = rlp::encode_bytes32_compact(key.value());
@@ -104,6 +110,8 @@ bool send_deletion(
104110
sizeof(addr),
105111
skey.data(),
106112
skey.size());
113+
++(*num_upserts);
114+
*upsert_bytes += sizeof(addr) + skey.size();
107115
}
108116
};
109117

@@ -118,6 +126,19 @@ bool send_deletion(
118126
bool statesync_server_handle_request(
119127
monad_statesync_server *const sync, monad_sync_request const rq)
120128
{
129+
uint64_t disk_ios_start = 0;
130+
uint64_t disk_bytes_start = 0;
131+
auto *const ctx = sync->context;
132+
auto &db = *ctx->ro;
133+
auto const *io = monad::async::AsyncIO::thread_instance();
134+
if (io != nullptr) {
135+
disk_ios_start = io->total_reads_submitted();
136+
disk_bytes_start = io->total_bytes_read();
137+
}
138+
139+
uint64_t num_upserts = 0;
140+
uint64_t upsert_bytes = 0;
141+
121142
struct Traverse final : public TraverseMachine
122143
{
123144
unsigned char nibble;
@@ -127,16 +148,21 @@ bool statesync_server_handle_request(
127148
NibblesView prefix;
128149
uint64_t from;
129150
uint64_t until;
151+
uint64_t *num_upserts;
152+
uint64_t *upsert_bytes;
130153

131154
Traverse(
132155
monad_statesync_server *const sync, NibblesView const prefix,
133-
uint64_t const from, uint64_t const until)
156+
uint64_t const from, uint64_t const until,
157+
uint64_t *const num_upserts, uint64_t *const upsert_bytes)
134158
: nibble{INVALID_BRANCH}
135159
, depth{0}
136160
, sync{sync}
137161
, prefix{prefix}
138162
, from{from}
139163
, until{until}
164+
, num_upserts{num_upserts}
165+
, upsert_bytes{upsert_bytes}
140166
{
141167
}
142168

@@ -188,13 +214,11 @@ bool statesync_server_handle_request(
188214
unsigned char const *const v1 =
189215
nullptr,
190216
uint64_t const size1 = 0) {
217+
uint64_t const size2 = node.value().size();
191218
sync->statesync_server_send_upsert(
192-
sync->net,
193-
type,
194-
v1,
195-
size1,
196-
node.value().data(),
197-
node.value().size());
219+
sync->net, type, v1, size1, node.value().data(), size2);
220+
++(*num_upserts);
221+
*upsert_bytes += size1 + size2;
198222
};
199223

200224
if (nibble == CODE_NIBBLE) {
@@ -253,8 +277,6 @@ bool statesync_server_handle_request(
253277
};
254278

255279
[[maybe_unused]] auto const start = std::chrono::steady_clock::now();
256-
auto *const ctx = sync->context;
257-
auto &db = *ctx->ro;
258280
if (rq.prefix < 256 && rq.target > rq.prefix) {
259281
auto const version = rq.target - rq.prefix - 1;
260282
NodeCursor const root{db.load_root_for_version(version)};
@@ -275,9 +297,11 @@ bool statesync_server_handle_request(
275297
val.size(),
276298
nullptr,
277299
0);
300+
++num_upserts;
301+
upsert_bytes += val.size();
278302
}
279303

280-
if (!send_deletion(sync, rq, *ctx)) {
304+
if (!send_deletion(sync, rq, *ctx, &num_upserts, &upsert_bytes)) {
281305
return false;
282306
}
283307

@@ -297,12 +321,25 @@ bool statesync_server_handle_request(
297321
}
298322

299323
[[maybe_unused]] auto const begin = std::chrono::steady_clock::now();
300-
Traverse traverse(sync, NibblesView{bytes}, rq.from, rq.until);
324+
Traverse traverse(
325+
sync,
326+
NibblesView{bytes},
327+
rq.from,
328+
rq.until,
329+
&num_upserts,
330+
&upsert_bytes);
301331
if (!db.traverse(finalized_root, traverse, rq.target)) {
302332
return false;
303333
}
304334
[[maybe_unused]] auto const end = std::chrono::steady_clock::now();
305335

336+
uint64_t disk_ios_submitted = 0;
337+
uint64_t disk_bytes_total = 0;
338+
if (io != nullptr) {
339+
disk_ios_submitted = io->total_reads_submitted() - disk_ios_start;
340+
disk_bytes_total = io->total_bytes_read() - disk_bytes_start;
341+
}
342+
306343
LOG_INFO(
307344
"processed request prefix={} prefix_bytes={} target={} from={} "
308345
"until={} "
@@ -316,6 +353,33 @@ bool statesync_server_handle_request(
316353
std::chrono::duration_cast<std::chrono::microseconds>(end - start),
317354
std::chrono::duration_cast<std::chrono::microseconds>(end - begin));
318355

356+
auto const elapsed_seconds =
357+
std::chrono::duration_cast<std::chrono::duration<double>>(end - start)
358+
.count();
359+
double disk_ios_per_sec = 0.0;
360+
double disk_bytes_per_sec = 0.0;
361+
double upsert_bytes_per_sec = 0.0;
362+
if (elapsed_seconds > 0.0) {
363+
disk_ios_per_sec =
364+
static_cast<double>(disk_ios_submitted) / elapsed_seconds;
365+
disk_bytes_per_sec =
366+
static_cast<double>(disk_bytes_total) / elapsed_seconds;
367+
upsert_bytes_per_sec =
368+
static_cast<double>(upsert_bytes) / elapsed_seconds;
369+
}
370+
371+
LOG_INFO(
372+
"session metrics: disk_ios={} disk_bytes={} num_upserts={} "
373+
"upsert_bytes={} | disk_ios/s={:.1f} disk_bytes/s={:.1f} "
374+
"upsert_bytes/s={:.1f}",
375+
disk_ios_submitted,
376+
disk_bytes_total,
377+
num_upserts,
378+
upsert_bytes,
379+
disk_ios_per_sec,
380+
disk_bytes_per_sec,
381+
upsert_bytes_per_sec);
382+
319383
return true;
320384
}
321385

category/statesync/test/test_statesync.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,7 @@ TEST_F(StateSyncFixture, benchmark)
10471047
handle_target(cctx, hdr);
10481048
run();
10491049
EXPECT_TRUE(monad_statesync_client_finalize(cctx));
1050+
quill::flush();
10501051
}
10511052

10521053
TEST(Deletions, history_length)

0 commit comments

Comments
 (0)