Skip to content

RFC for log segmentation - #32

Merged
hachikuji merged 7 commits into
mainfrom
log-segment-rfc
Jan 12, 2026
Merged

RFC for log segmentation#32
hachikuji merged 7 commits into
mainfrom
log-segment-rfc

Conversation

@hachikuji

@hachikuji hachikuji commented Jan 7, 2026

Copy link
Copy Markdown
Contributor

This patch adds a logical segmentation notion which is similar to the way that time buckets work in timeseries. Segments can be created based on time-based boundaries, such as hourly like with timeseries. The user can also seal a segment based on their own application logic. This provides a useful foundation for cross-key operations. For example, when reading from a range of keys, we can use the segment to position reads across all keys in the range. It could also be used as a basis for performing cross-key retention semantics (e.g. drop all segments older than one day).

@agavra agavra left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM, some food for thought before the green check though. Biggest concern is adding 8 bytes to each key.

Comment thread log/rfcs/0002-logical-segmentation.md Outdated
Comment thread log/rfcs/0002-logical-segmentation.md Outdated

```
Log Entry:
| version (u8) | type (u8) | segment_id (u64 BE) | key (TerminatedBytes) | sequence (u64 BE) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need a full u64 for segment id? u32 would allow 4B segments. adding this to every key seems pretty significant for a log structure. There are even varint encodings that maintain lexicographical ordering (see https://github.com/khonsulabs/ordered-varint for example, which we could easily vendor)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternatively, we can make sequence a u32 and make the total ordering segment + sequence so that we have u64 in total (sequences are unique within a segment)

@hachikuji hachikuji Jan 8, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree 8 bytes is a lot. Another idea I was considering is to add an attribute flag packed into the record type to specify the segment_id size. We could use 4 bytes by default, but switch to 8 bytes if needed. We probably wouldn't need to implement it until we need it. Just need to know it is possible.

@hachikuji hachikuji Jan 8, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like the idea to consider segment + sequence as a composite. One thing that bothers me a little bit is that we would have to bump when the u32 sequence is exhausted. Perhaps that's not a big deal, but I did kind of like the idea of representing "semantic segments" as we have with timeseries (each bucket represents an hour of time). It would be annoying to have to handle overflow segments. Perhaps we could consider using varlength u64 for the sequence number which always resets to 0? So u32 segment_id and varlength u64 sequence. This option wasn't possible until we had the fancypants delimiter for the key.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

We could use 4 bytes by default, but switch to 8 bytes if needed.
So u32 segment_id and varlength u64 sequence

I like both of these ideas! I agree that dealing with overflow-triggering segment bumps is not fun.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the concern the storage overhead? I would imagine that gets optimized away pretty well by the prefix encoding in the SSTs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that + memory overhead, I guess prefix compression would basically eliminate storage overhead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented something close to what we discussed. First, I switched to a u32 segment encoding, Second, I changed the sequence number to be a varlength u64 which is defined relative to the base sequence from the SegmentMetadata.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's perfect since we'll get great prefix compression from the segment in slate but actually pretty bad compression from the sequence number so this may turn out to be better overall than the original design 👍

Comment thread log/rfcs/0002-logical-segmentation.md Outdated
Comment thread log/rfcs/0002-logical-segmentation.md Outdated

1. **On open**: When a new segment is created, a `SegmentMeta` record is written with `start_seq`, `start_time_ms`, and empty `user_meta`. This ensures the segment is immediately discoverable.

2. **On seal**: When `seal_segment()` is called with user metadata, the `SegmentMeta` record is overwritten to include the user-provided bytes. If no user metadata is provided, the record is left unchanged.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably need to track the current segment id and increment it somewhere, right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just read the metadata in reverse order to find the last segment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of expected we would keep the segment metadata in memory. It's tiny.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, I had a brain fart that we could just compute it from the latest SST last key on restart

Comment thread log/rfcs/0002-logical-segmentation.md

@rodesai rodesai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me. One high level thought is whether the notion of a segment is something we should just bake into slatedb directly. It feels very similar to what we'd want with twcs. Need to think more about what that would look like.

Comment thread log/rfcs/0002-logical-segmentation.md Outdated

```
Log Entry:
| version (u8) | type (u8) | segment_id (u64 BE) | key (TerminatedBytes) | sequence (u64 BE) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the concern the storage overhead? I would imagine that gets optimized away pretty well by the prefix encoding in the SSTs

Comment thread log/rfcs/0002-logical-segmentation.md Outdated

```
SegmentMeta Record:
Key: | version (u8) | type (u8=0x03) | segment_id (u64 BE) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thought: does it make sense to include the notion of a segment key and a segment id, and use the id as the key for internally generated segments? The idea being that when we support user-generated segments they can select their own keys that can be looked up later

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a nice idea. Do you think this could be added through a version bump later? Another interesting idea I was considering is to allow segments to be defined at prefix/range granularity. That might open the door to enforcing different retention semantics for different keys. I do think there might an interesting application design space here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are neat ideas but I'm +1 to keeping things as simple as possible and only tinroducing those concepts if necessary later to avoid complicating things

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, sgtm

Comment thread log/rfcs/0002-logical-segmentation.md Outdated

1. **On open**: When a new segment is created, a `SegmentMeta` record is written with `start_seq`, `start_time_ms`, and empty `user_meta`. This ensures the segment is immediately discoverable.

2. **On seal**: When `seal_segment()` is called with user metadata, the `SegmentMeta` record is overwritten to include the user-provided bytes. If no user metadata is provided, the record is left unchanged.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just read the metadata in reverse order to find the last segment.

@hachikuji

Copy link
Copy Markdown
Contributor Author

One high level thought is whether the notion of a segment is something we should just bake into slatedb directly. It feels very similar to what we'd want with twcs.

@rodesai I agree that is the interesting question. I was wondering if the API in slatedb could work with minimal assumptions about key structure. For example, maybe we could have an API to mark a range of keys as "sealed" which makes those keys immutable and stops compaction? I think we need a design in slate sooner than later. Not sure this work needs to block behind it though?

@rodesai

rodesai commented Jan 10, 2026

Copy link
Copy Markdown
Contributor

Not sure this work needs to block behind it though?

Totally agree. We can move forward with the approach here and revisit as we understand the problem better.

@hachikuji

Copy link
Copy Markdown
Contributor Author

If there are no objections, I'll merge this.

@rodesai rodesai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hachikuji
hachikuji merged commit 4a84782 into main Jan 12, 2026
1 check passed
@hachikuji
hachikuji deleted the log-segment-rfc branch January 12, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants