Expose C_SEGMENT_SIZE_BYTES gauges - #218
Conversation
| %% local segments | ||
| [begin ok = delete_segment_from_index(I) end | ||
| || I <- IdxFiles], | ||
| [delete_segment_from_index(I) || I <- IdxFiles], |
There was a problem hiding this comment.
Not sure we pattern match from above is really necessary?
There was a problem hiding this comment.
Yeah looks like it isn't since delete_segment_from_index/1 already hard matches on ok
the-mikedavis
left a comment
There was a problem hiding this comment.
I took a look and I have a few nits but I think this is the right approach. I'm not an authority here so I'd wait for feedback from Arnaud / David / Karl before acting on my opinions (especially the index file part).
| {segments, ?C_SEGMENTS, counter, "Number of segments"} | ||
| {segments, ?C_SEGMENTS, counter, "Number of segments"}, | ||
| {segment_size_bytes, ?C_SEGMENT_SIZE_BYTES, counter, "Total size of all segment files in bytes"}, | ||
| {index_size_bytes, ?C_INDEX_SIZE_BYTES, counter, "Total size of all index files in bytes"} |
There was a problem hiding this comment.
I'm not sure it's worthwhile to track index data. It would mean an extra fstat for index files during recovery and retention to gather the size data. Since chunk headers are at minimum 48 bytes per chunk, the segment will always be larger (48 > 29 for each index record). If these metrics aim to answer a question of "which stream is taking up all of my data" I think segment size metrics are enough.
I think there's a separate question of "are my chunks so small they're hurting performance" and that could be answered by a bytes-per-chunk metric instead. Tracking index data here would indirectly hint towards that rather than answer it clearly.
There was a problem hiding this comment.
I am not strong on any claim here as I also believe that for big clusters, with loads of streams, that's not a cheap price to pay.
There was a problem hiding this comment.
@the-mikedavis
To track or not to track it: I thought maybe, if the overhead is not big, to keep going with both. Easier to catch bugs if users have this level of visibility.
Now that was myself assuming there's a chance the index file could have holes/get bloated somehow - which could have been naive as Streams are running for lots of years and there's not a single complaint about it.
I'll good with removing the index tracking from this PR
| %% local segments | ||
| [begin ok = delete_segment_from_index(I) end | ||
| || I <- IdxFiles], | ||
| [delete_segment_from_index(I) || I <- IdxFiles], |
There was a problem hiding this comment.
Yeah looks like it isn't since delete_segment_from_index/1 already hard matches on ok
| {segments, ?C_SEGMENTS, counter, "Number of segments"} | ||
| {segments, ?C_SEGMENTS, counter, "Number of segments"}, | ||
| {segment_size_bytes, ?C_SEGMENT_SIZE_BYTES, counter, "Total size of all segment files in bytes"}, | ||
| {index_size_bytes, ?C_INDEX_SIZE_BYTES, counter, "Total size of all index files in bytes"} |
There was a problem hiding this comment.
It looks like all of these are counter currently but I think most of them (maybe all of them?) should be gauge. Maybe it's outside the scope of this change though since the existing ones all use counter
There was a problem hiding this comment.
Pull request overview
This PR adds two new size metrics to Osiris logs—total segment bytes and total index bytes—so they can be surfaced in the Management UI and exported to Prometheus. The metrics are initialized on startup via filesystem stat, incremented on writes, and decremented after retention deletes segments.
Changes:
- Extend
osiris_logcounters withsegment_size_bytesandindex_size_bytes, including initialization, write-time increments, and retention-time decrements. - Extend retention evaluation to return deleted segment/index byte counts so stream processes can update the new metrics.
- Add Common Test coverage for fresh logs, writes, restart, and retention effects on the new size counters.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
src/osiris_log.erl |
Adds the two new counters, initializes them at init, updates them on write, and decrements them based on retention deletions. |
src/osiris_retention.erl |
Adjusts scheduling pattern match to handle the extended retention evaluation return value. |
src/osiris.hrl |
Increases the base log counter field count to accommodate the two new counters. |
test/osiris_log_SUITE.erl |
Adds tests validating the new counters across init/write/restart/retention flows and adapts retention tests to new return shape. |
test/osiris_SUITE.erl |
Adds an integration test ensuring retention decreases segment_size_bytes at the writer counter level. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
the-mikedavis
left a comment
There was a problem hiding this comment.
This is looking good to me 👍. I only have nitpicks / style comments left.
Before merging though I think we should discuss/decide whether index file bytes are worthy of tracking. I would lean against it but I don't have a strong opinion either way.
- Restored comments about future performance improvements - Removed Index Tracking - Moved to Maps instead of Tuple
C_SEGMENT_SIZE_BYTES and C_INDEX_SIZE_BYTES gaugesC_SEGMENT_SIZE_BYTES gauges
the-mikedavis
left a comment
There was a problem hiding this comment.
Looks great to me 👍
|
Hi @kjnilsson Any estimates for this? Just so I can snooze this notification for a while |
|
Tick the box to add this pull request to the merge queue (same as
|
| counters:put(Cnt, ?C_FIRST_TIMESTAMP, FstTs), | ||
| counters:put(Cnt, ?C_SEGMENTS, NumSegLeft); | ||
| counters:put(Cnt, ?C_SEGMENTS, NumSegLeft), | ||
| counters:sub(Cnt, ?C_SEGMENT_SIZE_BYTES, DelSegBytes); |
kjnilsson
left a comment
There was a problem hiding this comment.
some performance regressions that could be improved.
| %% at a valid chunk we can now truncate the segment to size in | ||
| %% case there is trailing data | ||
| ok = file:truncate(SegFd), | ||
| InitSegBytes = sum_log_sizes(Config), |
There was a problem hiding this comment.
this adds an additional file listing operation (which is O(N)) - at least we should be able to use the same listing for first_and_last_seginfos and this by adding the index_files key to the Config before that (but after maybe_fix_corrupted_files/1 as this may change the files on disk.
| delete_segment_from_index(Index) -> | ||
| File = segment_from_index_file(Index), | ||
| ?DEBUG("osiris_log: deleting segment ~ts", [File]), | ||
| SegSize = file_size_or_zero(File), |
There was a problem hiding this comment.
this incurs an additional syscall for all calls of this function, even if they don't make use of it. Perhaps we need two different functions or the file size if got independently where needed.
There was a problem hiding this comment.
I've used the Size from previous read. What I am not completely sure is if, there's any chance the segment size would change between the first stat and the delete
The fold ignored its element and deleted the first index file N times while summing its size N times, leaving older segments undeleted and under-reporting deleted_segment_bytes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What
Add both metrics for C_SEGMENT_SIZE_BYTES so we can enable on Management UI and Prometheus.
How
statat each file atinitBelow is AI-generated
Benchmark with Mac Book M3 Max PRO (32GB RAM 10-4 CPU)
sum_log_sizesis 101 × 2 stat calls, negligibleThe 406 ms on the first retention run is the actual file deletion of 51 segments. Runs 2–5 (index scan only, nothing to delete) are 2–3 ms regardless of how many segments remain — that confirms the O(remaining) scan is cheap at this scale.
At 100 GB you'd expect ~800 ms for a full 50% delete run, and the same 2–4 ms for idempotent evaluations. Nothing alarming.
Closes #161