Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR implements streaming compaction for libsql-wal.
Motivation
Before, we used buffer files for segment compaction. When long sequences of segments needed a lot of disk storage to store intermediate segments and the resulting segments. With some changes to the compacted segment format this PR enables the streaming compaction of segments.
How?
Initially, the
CompactedSegments
contained a header with the number of frames in the segment. When compacting we can't cheaply know how many frames will be in the resulting segment, so we needed a different way know how many frames there are in the segment. The segment also contained a footer with the checksum, but without the frame count, it's impossible to know where to fetch the footer. Instead, we change the frame headers, introducing aCompactedFrameHeader
. The compacted frame headers drop the size_after (all frames in a compacted segment should be logically committed together) field, and introduce a checksum field, and a flag field, with theLAST
flag. TheLAST
flag is set for the last flag in the segment.The checksum is computed as the crc32 frame header + data (expect the checksum), seeded by the checksum of the previous frame. The first frame is seeded with the checksum of the segment header.
The
dedup_stream
method in the compactor is the meat of this PR. It takes aSegmentSet
, and returns a deduplicated set of all the frame for that set. Here's how it works:Iterating on the segments in the set backwards (most recent segment first), we start downloading indexed (this step is done conccurently). Then, we sequentially iterate over the received segments, and check if that segment contains any data that we need. To do this, the maintain a
seen_pages
bitset with all the pages we have already collected. If any page in the segment index is not in the set, we download the segment data. For every frame in the segment data whose page we haven't seen, we stream that page out. We repeat this process, until we either have enough pages (as indicated by size after), or run out ot segments to search in.