diff --git a/internal/keyspan/interleaving_iter.go b/internal/keyspan/interleaving_iter.go index 7859e233cd..85df84d755 100644 --- a/internal/keyspan/interleaving_iter.go +++ b/internal/keyspan/interleaving_iter.go @@ -87,6 +87,11 @@ type SpanMask interface { // InterleavedIter does not interleave synthetic markers for spans that do not // contain any keys. // +// When InterleavingIterOpts.InterleaveEndKeys is set, in addition to +// interleaving start keys, the interleaving iterator will interleave end +// boundary keys (also at the maximumal sequence number). At these end boundary +// positions, Span() will return the span to which the end boundary belongs. +// // # SpanMask // // InterelavingIter takes a SpanMask parameter that may be used to configure the @@ -190,6 +195,10 @@ var _ base.InternalIterator = &InterleavingIter{} type InterleavingIterOpts struct { Mask SpanMask LowerBound, UpperBound []byte + // InterleaveEndKeys configures the interleaving iterator to interleave the + // end keys of spans (in addition to the start keys, which are always + // interleaved). + InterleaveEndKeys bool } // Init initializes the InterleavingIter to interleave point keys from pointIter @@ -792,16 +801,19 @@ func (i *InterleavingIter) yieldPosition(lowerBound []byte, advance func()) *bas } return i.yieldPointKey() case posKeyspanEnd: - // Don't interleave end keys; just advance. - advance() - continue + if !i.opts.InterleaveEndKeys { + // Don't interleave end keys; just advance. + advance() + continue + } + return i.yieldSyntheticSpanEndMarker() case posKeyspanStart: // Don't interleave an empty span. if i.span.Empty() { advance() continue } - return i.yieldSyntheticSpanMarker(lowerBound) + return i.yieldSyntheticSpanStartMarker(lowerBound) default: panic(fmt.Sprintf("unexpected interleavePos=%d", i.pos)) } @@ -939,7 +951,7 @@ func (i *InterleavingIter) yieldPointKey() *base.InternalKV { return i.verify(i.pointKV) } -func (i *InterleavingIter) yieldSyntheticSpanMarker(lowerBound []byte) *base.InternalKV { +func (i *InterleavingIter) yieldSyntheticSpanStartMarker(lowerBound []byte) *base.InternalKV { i.spanMarker.K.UserKey = i.startKey() i.spanMarker.K.Trailer = base.MakeTrailer(base.InternalKeySeqNumMax, i.span.Keys[0].Kind()) @@ -976,6 +988,12 @@ func (i *InterleavingIter) yieldSyntheticSpanMarker(lowerBound []byte) *base.Int return i.verify(&i.spanMarker) } +func (i *InterleavingIter) yieldSyntheticSpanEndMarker() *base.InternalKV { + i.spanMarker.K.UserKey = i.endKey() + i.spanMarker.K.Trailer = base.MakeTrailer(base.InternalKeySeqNumMax, i.span.Keys[0].Kind()) + return i.verify(&i.spanMarker) +} + func (i *InterleavingIter) disablePrefixMode() { if i.prefix != nil { i.prefix = nil @@ -994,7 +1012,8 @@ func (i *InterleavingIter) verify(kv *base.InternalKV) *base.InternalKV { panic("pebble: invariant violation: truncated span key in reverse iteration") case kv != nil && i.opts.LowerBound != nil && i.cmp(kv.K.UserKey, i.opts.LowerBound) < 0: panic("pebble: invariant violation: key < lower bound") - case kv != nil && i.opts.UpperBound != nil && i.cmp(kv.K.UserKey, i.opts.UpperBound) >= 0: + case kv != nil && i.opts.UpperBound != nil && + !base.UserKeyExclusive(i.opts.UpperBound).IsUpperBoundForInternalKey(i.comparer.Compare, kv.K): panic("pebble: invariant violation: key ≥ upper bound") case i.err != nil && kv != nil: panic("pebble: invariant violation: accumulated error swallowed") @@ -1043,6 +1062,13 @@ func (i *InterleavingIter) startKey() []byte { return i.span.Start } +func (i *InterleavingIter) endKey() []byte { + if i.truncated { + return i.truncatedSpan.End + } + return i.span.End +} + func (i *InterleavingIter) savePoint(kv *base.InternalKV) { i.pointKV = kv if kv == nil { diff --git a/internal/keyspan/interleaving_iter_test.go b/internal/keyspan/interleaving_iter_test.go index 36e872f572..f9d4df4674 100644 --- a/internal/keyspan/interleaving_iter_test.go +++ b/internal/keyspan/interleaving_iter_test.go @@ -122,6 +122,7 @@ func runInterleavingIterTest(t *testing.T, filename string) { if cmdArg, ok := td.Arg("masking-threshold"); ok { hooks.threshold = []byte(strings.Join(cmdArg.Vals, "")) } + opts.InterleaveEndKeys = td.HasArg("interleave-end-keys") iter.Init(testkeys.Comparer, &pointIter, keyspanIter, opts) // Clear any previous bounds. pointIter.SetBounds(nil, nil) diff --git a/internal/keyspan/testdata/interleaving_iter b/internal/keyspan/testdata/interleaving_iter index 3b0a9543f6..c1c79ddf64 100644 --- a/internal/keyspan/testdata/interleaving_iter +++ b/internal/keyspan/testdata/interleaving_iter @@ -78,6 +78,96 @@ PointKey: tomato#2,SET Span: q-z:{(#14,RANGEKEYSET,@9,mangos)} - +# Test interleaving end keys. + +iter interleave-end-keys +first +next +next +next +next +next +next +next +next +next +next +next +next +next +next +next +next +next +next +---- +-- SpanChanged(nil) +-- SpanChanged(a-c:{(#10,RANGEKEYSET,@5,apples) (#10,RANGEKEYDEL) (#8,RANGEKEYUNSET,@1) (#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@2,oranges)}) +PointKey: a#72057594037927935,RANGEKEYSET +Span: a-c:{(#10,RANGEKEYSET,@5,apples) (#10,RANGEKEYDEL) (#8,RANGEKEYUNSET,@1) (#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@2,oranges)} +- +PointKey: artichoke#10,SET +Span: a-c:{(#10,RANGEKEYSET,@5,apples) (#10,RANGEKEYDEL) (#8,RANGEKEYUNSET,@1) (#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@2,oranges)} +- +PointKey: artichoke#8,SET +Span: a-c:{(#10,RANGEKEYSET,@5,apples) (#10,RANGEKEYDEL) (#8,RANGEKEYUNSET,@1) (#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@2,oranges)} +- +PointKey: c#72057594037927935,RANGEKEYSET +Span: a-c:{(#10,RANGEKEYSET,@5,apples) (#10,RANGEKEYDEL) (#8,RANGEKEYUNSET,@1) (#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@2,oranges)} +- +-- SpanChanged(c-d:{(#4,RANGEKEYSET,@3,coconut)}) +PointKey: c#72057594037927935,RANGEKEYSET +Span: c-d:{(#4,RANGEKEYSET,@3,coconut)} +- +PointKey: carrot#13,SET +Span: c-d:{(#4,RANGEKEYSET,@3,coconut)} +- +PointKey: cauliflower#9,DEL +Span: c-d:{(#4,RANGEKEYSET,@3,coconut)} +- +PointKey: d#72057594037927935,RANGEKEYSET +Span: c-d:{(#4,RANGEKEYSET,@3,coconut)} +- +-- SpanChanged(e-f:{(#20,RANGEKEYSET,@5,pineapple) (#20,RANGEKEYSET,@3,guava)}) +PointKey: e#72057594037927935,RANGEKEYSET +Span: e-f:{(#20,RANGEKEYSET,@5,pineapple) (#20,RANGEKEYSET,@3,guava)} +- +PointKey: f#72057594037927935,RANGEKEYSET +Span: e-f:{(#20,RANGEKEYSET,@5,pineapple) (#20,RANGEKEYSET,@3,guava)} +- +-- SpanChanged(h-j:{(#22,RANGEKEYDEL) (#21,RANGEKEYSET,@5,peaches) (#21,RANGEKEYSET,@3,starfruit)}) +PointKey: h#72057594037927935,RANGEKEYDEL +Span: h-j:{(#22,RANGEKEYDEL) (#21,RANGEKEYSET,@5,peaches) (#21,RANGEKEYSET,@3,starfruit)} +- +PointKey: j#72057594037927935,RANGEKEYDEL +Span: h-j:{(#22,RANGEKEYDEL) (#21,RANGEKEYSET,@5,peaches) (#21,RANGEKEYSET,@3,starfruit)} +- +-- SpanChanged(l-m:{(#2,RANGEKEYUNSET,@9) (#2,RANGEKEYUNSET,@5)}) +PointKey: l#72057594037927935,RANGEKEYUNSET +Span: l-m:{(#2,RANGEKEYUNSET,@9) (#2,RANGEKEYUNSET,@5)} +- +PointKey: m#72057594037927935,RANGEKEYUNSET +Span: l-m:{(#2,RANGEKEYUNSET,@9) (#2,RANGEKEYUNSET,@5)} +- +-- SpanChanged(nil) +PointKey: parsnip#3,SET +Span: +- +-- SpanChanged(q-z:{(#14,RANGEKEYSET,@9,mangos)}) +PointKey: q#72057594037927935,RANGEKEYSET +Span: q-z:{(#14,RANGEKEYSET,@9,mangos)} +- +PointKey: tomato#2,SET +Span: q-z:{(#14,RANGEKEYSET,@9,mangos)} +- +PointKey: z#72057594037927935,RANGEKEYSET +Span: q-z:{(#14,RANGEKEYSET,@9,mangos)} +- +-- SpanChanged(nil) +PointKey: zucchini#12,MERGE +Span: +- + # Test set-bounds passes through to the underlying point iterator and truncates # a range key's end. @@ -996,3 +1086,226 @@ Span: f-g:{(#6,RANGEKEYSET,@9,foo)} -- SpanChanged(nil) . . + +define-spans +b-e:{(#5,RANGEDEL)} +f-g:{(#6,RANGEDEL)} +---- +OK + +define-pointkeys +a@4.SET.8 +c@11.SET.8 +c@3.SET.8 +c@1.SET.4 +d@5.SET.3 +e@9.SET.2 +---- +OK + +iter interleave-end-keys +first +next +next +next +next +next +next +next +next +next +next +---- +-- SpanChanged(nil) +-- SpanChanged(nil) +PointKey: a@4#8,SET +Span: +- +-- SpanChanged(b-e:{(#5,RANGEDEL)}) +PointKey: b#72057594037927935,RANGEDEL +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: c@11#8,SET +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: c@3#8,SET +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: c@1#4,SET +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: d@5#3,SET +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: e#72057594037927935,RANGEDEL +Span: b-e:{(#5,RANGEDEL)} +- +-- SpanChanged(nil) +PointKey: e@9#2,SET +Span: +- +-- SpanChanged(f-g:{(#6,RANGEDEL)}) +PointKey: f#72057594037927935,RANGEDEL +Span: f-g:{(#6,RANGEDEL)} +- +PointKey: g#72057594037927935,RANGEDEL +Span: f-g:{(#6,RANGEDEL)} +- +-- SpanChanged(nil) +. + +iter interleave-end-keys +last +prev +prev +prev +prev +prev +prev +prev +prev +prev +prev +---- +-- SpanChanged(nil) +PointKey: g#72057594037927935,RANGEDEL +Span: f-g:{(#6,RANGEDEL)} +- +-- SpanChanged(f-g:{(#6,RANGEDEL)}) +PointKey: f#72057594037927935,RANGEDEL +Span: f-g:{(#6,RANGEDEL)} +- +-- SpanChanged(nil) +PointKey: e@9#2,SET +Span: +- +PointKey: e#72057594037927935,RANGEDEL +Span: b-e:{(#5,RANGEDEL)} +- +-- SpanChanged(b-e:{(#5,RANGEDEL)}) +PointKey: d@5#3,SET +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: c@1#4,SET +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: c@3#8,SET +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: c@11#8,SET +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: b#72057594037927935,RANGEDEL +Span: b-e:{(#5,RANGEDEL)} +- +-- SpanChanged(nil) +PointKey: a@4#8,SET +Span: +- +-- SpanChanged(nil) +. + +iter interleave-end-keys +seek-ge c@1 +next +---- +-- SpanChanged(nil) +-- SpanChanged(b-e:{(#5,RANGEDEL)}) +PointKey: c@1#72057594037927935,RANGEDEL +Span: b-e:{(#5,RANGEDEL)} +- +PointKey: c@1#4,SET +Span: b-e:{(#5,RANGEDEL)} +- + +iter interleave-end-keys +seek-lt c@10 +prev +---- +-- SpanChanged(nil) +PointKey: e#72057594037927935,RANGEDEL +Span: b-e:{(#5,RANGEDEL)} +- +-- SpanChanged(b-e:{(#5,RANGEDEL)}) +PointKey: c@11#8,SET +Span: b-e:{(#5,RANGEDEL)} +- + +# Test abutting spans. + +define-spans +a-b:{(#5,RANGEDEL)} +b-c:{(#6,RANGEDEL)} +---- +OK + +define-pointkeys +a@4.SET.8 +b@9.DEL.2 +c@11.SET.8 +c@3.SET.8 +---- +OK + +iter interleave-end-keys +seek-ge a +next +next +next +next +---- +-- SpanChanged(nil) +-- SpanChanged(a-b:{(#5,RANGEDEL)}) +PointKey: a#72057594037927935,RANGEDEL +Span: a-b:{(#5,RANGEDEL)} +- +PointKey: a@4#8,SET +Span: a-b:{(#5,RANGEDEL)} +- +PointKey: b#72057594037927935,RANGEDEL +Span: a-b:{(#5,RANGEDEL)} +- +-- SpanChanged(b-c:{(#6,RANGEDEL)}) +PointKey: b#72057594037927935,RANGEDEL +Span: b-c:{(#6,RANGEDEL)} +- +PointKey: b@9#2,DEL +Span: b-c:{(#6,RANGEDEL)} +- + +iter interleave-end-keys +seek-ge a@9 +next +next +---- +-- SpanChanged(nil) +-- SpanChanged(a-b:{(#5,RANGEDEL)}) +PointKey: a@9#72057594037927935,RANGEDEL +Span: a-b:{(#5,RANGEDEL)} +- +PointKey: a@4#8,SET +Span: a-b:{(#5,RANGEDEL)} +- +PointKey: b#72057594037927935,RANGEDEL +Span: a-b:{(#5,RANGEDEL)} +- + +iter interleave-end-keys +seek-lt a@1 +prev +prev +prev +---- +-- SpanChanged(nil) +PointKey: b#72057594037927935,RANGEDEL +Span: a-b:{(#5,RANGEDEL)} +- +-- SpanChanged(a-b:{(#5,RANGEDEL)}) +PointKey: a@4#8,SET +Span: a-b:{(#5,RANGEDEL)} +- +PointKey: a#72057594037927935,RANGEDEL +Span: a-b:{(#5,RANGEDEL)} +- +-- SpanChanged(nil) +.