-
Notifications
You must be signed in to change notification settings - Fork 1.6k
fix(posting): mark removed split parts for reclamation #9825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1471,6 +1471,20 @@ func (l *List) Rollup(alloc *z.Allocator, readTs uint64) ([]*bpb.KV, error) { | |
| kvs = append(kvs, kv) | ||
| } | ||
|
|
||
| // Retire removed parts at the parent's timestamp (+1 for finite readTs) | ||
| // so compaction preserves reads using the old parent. | ||
| for _, startUid := range l.plist.Splits { | ||
| if _, retained := out.parts[startUid]; retained { | ||
| continue | ||
| } | ||
| part, err := out.marshalPostingListPart(alloc, l.key, startUid, &pb.PostingList{}) | ||
| if err != nil { | ||
| return nil, errors.Wrapf(err, "cannot marshal removed posting list part") | ||
| } | ||
| part.Version = kv.Version | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This works only because the retained-parts loop above shadows While you're at it, a line on why removed parts get the parent's |
||
| kvs = append(kvs, part) | ||
| } | ||
|
|
||
| // Sort the KVs by their key so that the main part of the list is at the | ||
| // start of the list and all other parts appear in the order of their start UID. | ||
| sort.Slice(kvs, func(i, j int) bool { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The crash-safety of this depends on a write-ordering invariant that's currently implicit. If one of these tombstones ever became durable before the new parent KV and the process died in between, the old parent would still be the latest visible version and would reference a part that now reads as empty. Worse, the next rollup would read that part as empty and persist the loss.
Today the ordering holds: the sort below puts the main key first (
DefaultPrefix0x00 sorts beforeByteSplit0x04),TxnWritersubmits commits in call order, and badger's WAL replays a strict prefix, so a tombstone can't survive a crash without its parent. But nothing in the code says that order is load-bearing (the sort comment only mentions readability). Could you add a short comment here or on the sort noting that the main-list KV must be written before these tombstones?