# Set a key within the indexed batch. new-batch set foo foo ---- # Construct an iterator over the indexed batch. new-batch-iter i0 ---- # The key we set should be visible. iter iter=i0 first next ---- foo: (foo, .) . # Same behavior with a batch-only iterator. new-batch-only-iter i-bo0 ---- iter iter=i-bo0 first next ---- foo: (foo, .) . # Set a new key, while the above iterator is still open. mutate set bar bar ---- # The new key should be invisible. iter iter=i0 prev next ---- foo: (foo, .) . # Same behavior with a batch-only iterator. iter iter=i-bo0 prev next ---- foo: (foo, .) . # A set-options operation should refresh the Iterator's view of the batch. The # bar key should now be visibile. iter iter=i0 set-options first next next ---- . bar: (bar, .) foo: (foo, .) . # Same behavior with a batch-only iterator. iter iter=i-bo0 set-options first next next ---- . bar: (bar, .) foo: (foo, .) . # Delete foo with a range deletion. mutate del-range f g ---- # Both keys should still be visible. iter iter=i0 prev prev ---- foo: (foo, .) bar: (bar, .) # Same behavior with a batch-only iterator. iter iter=i-bo0 prev prev ---- foo: (foo, .) bar: (bar, .) # After refreshing the iterator's view of the batch, foo should be deleted. iter iter=i0 set-options seek-ge foo seek-lt foo ---- . . bar: (bar, .) # Same behavior with a batch-only iterator. iter iter=i-bo0 set-options seek-ge foo seek-lt foo ---- . . bar: (bar, .) # Write a range key set and a point key. mutate range-key-set a c @1 boop set b b ---- # The mutations should not be visible. iter iter=i0 prev next ---- . bar: (bar, .) # Same behavior with a batch-only iterator. iter iter=i-bo0 prev next ---- . bar: (bar, .) # But refreshing the batch through a call to SetOptions should surface them. iter iter=i0 set-options first next next ---- . a: (., [a-c) @1=boop UPDATED) b: (b, [a-c) @1=boop) bar: (bar, [a-c) @1=boop) # Same behavior with a batch-only iterator. iter iter=i-bo0 set-options first next next ---- . a: (., [a-c) @1=boop UPDATED) b: (b, [a-c) @1=boop) bar: (bar, [a-c) @1=boop) # Remove part of the range key to fragment it. mutate range-key-del ace arc ---- iter iter=i0 next prev prev prev prev ---- . bar: (bar, [a-c) @1=boop UPDATED) b: (b, [a-c) @1=boop) a: (., [a-c) @1=boop) . # Same behavior with a batch-only iterator. iter iter=i-bo0 next prev prev prev prev ---- . bar: (bar, [a-c) @1=boop UPDATED) b: (b, [a-c) @1=boop) a: (., [a-c) @1=boop) . iter iter=i0 set-options first next next next ---- . a: (., [a-ace) @1=boop UPDATED) arc: (., [arc-c) @1=boop UPDATED) b: (b, [arc-c) @1=boop) bar: (bar, [arc-c) @1=boop) # Same behavior with a batch-only iterator. iter iter=i-bo0 set-options first next next next ---- . a: (., [a-ace) @1=boop UPDATED) arc: (., [arc-c) @1=boop UPDATED) b: (b, [arc-c) @1=boop) bar: (bar, [arc-c) @1=boop) # Create a new indexed batch and a new iterator over it. new-batch set foo foo ---- new-batch-iter i1 ---- iter iter=i1 first next ---- foo: (foo, .) . # Test interactions with cloned iterators. # First, apply mutations to the batch. They should remain invisible. mutate set bar bar range-key-set a z @1 boop del-range f g ---- iter iter=i1 first next ---- foo: (foo, .) . # Clone i1 to create i2. clone from=i1 to=i2 refresh-batch=false ---- # i1 unchanged. iter iter=i1 first next ---- foo: (foo, .) . # i2 sees exactly the same stale state as i1 until SetOptions is called to # explicitly refresh the view of the underlying batch. iter iter=i2 first next set-options first next next ---- foo: (foo, .) . . a: (., [a-z) @1=boop UPDATED) bar: (bar, [a-z) @1=boop) . # Clone i1 to create i3, this time passing RefreshBatchView: true. This clone # should view the updated view of the underlying batch. clone from=i1 to=i3 refresh-batch=true ---- iter iter=i3 first next ---- a: (., [a-z) @1=boop UPDATED) bar: (bar, [a-z) @1=boop) # i1 should still have the old, stale view of the batch. iter iter=i1 first next ---- foo: (foo, .) . # Mutate the underlying batch again. mutate set foo foo range-key-set a z @2 bax del-range b c ---- # The new mutations should be invisible until SetOptions is called. iter iter=i1 first next set-options first next next ---- foo: (foo, .) . . a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . iter iter=i2 first next next set-options first next next ---- a: (., [a-z) @1=boop UPDATED) bar: (bar, [a-z) @1=boop) . . a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . # Commit a separate batch to the underlying engine. batch range-key-set a z @5 poi set apple apple ---- # The writes to the underlying engine should be invisible. iter iter=i1 first next next ---- a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . # Clone i1 to create i4. clone from=i1 to=i4 refresh-batch=false ---- iter iter=i4 first next next ---- a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . # Refresh i4's view of its batch. It should still not see the newly committed # writes. iter iter=i4 set-options first next next ---- . a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . # Create a new iterator i5 over the indexed batch [not a Clone]. It should see # all committed writes and uncommitted writes. new-batch-iter i5 ---- iter iter=i5 first next next next ---- a: (., [a-z) @5=poi, @2=bax, @1=boop UPDATED) apple: (apple, [a-z) @5=poi, @2=bax, @1=boop) foo: (foo, [a-z) @5=poi, @2=bax, @1=boop) . # The batch-only iter only sees the contents of the batch. new-batch-only-iter i-bo1 ---- iter iter=i-bo1 first next next ---- a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . # Mutate all the open iterators' underlying batch. mutate range-key-set a z @6 yaya set c c ---- # The iterators should still not see the committed writes, even after refreshing # to observe more recent batch writes. iter iter=i1 first next next ---- a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . iter iter=i4 first next next set-options first next next ---- a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . . a: (., [a-z) @6=yaya, @2=bax, @1=boop UPDATED) c: (c, [a-z) @6=yaya, @2=bax, @1=boop) foo: (foo, [a-z) @6=yaya, @2=bax, @1=boop) # The batch-only iter sees the more recent batch writes after refreshing. iter iter=i-bo1 first next next set-options first next next next ---- a: (., [a-z) @2=bax, @1=boop UPDATED) foo: (foo, [a-z) @2=bax, @1=boop) . . a: (., [a-z) @6=yaya, @2=bax, @1=boop UPDATED) c: (c, [a-z) @6=yaya, @2=bax, @1=boop) foo: (foo, [a-z) @6=yaya, @2=bax, @1=boop) . # Test a scenario where constructing an Iterator should NOT use the cached # fragmented tombstones / range keys, because the new Iterator is a Clone which # must read at an earlier batch sequence number. # Reset and start a new batch. reset ---- new-batch set foo foo ---- new-batch-iter i1 ---- iter iter=i1 first next ---- foo: (foo, .) . # Apply a range deletion and a range key. mutate del-range a z range-key-set a z @1 foo ---- # Create a new iterator which will see both the range deletion and the range # key, and cache both on the batch so that future iterators constructed over the # batch do not need to. new-batch-iter i2 ---- iter iter=i2 first next ---- a: (., [a-z) @1=foo UPDATED) . # Clone the original iterator from before the delete range and the range key # were created. It should not use the cached fragments of range deletions or # range keys, and should not see the effects of either. clone from=i1 to=i3 refresh-batch=false ---- iter iter=i3 first next ---- foo: (foo, .) . reset ---- new-batch range-key-set a c @1 poi range-key-set b d @2 yaya ---- new-batch-iter i1 ---- # The batch contains 2 range keys, but the skiplist of fragmented range keys # contains 3 elements (a-b, b-c, c-d). iter iter=i1 first next next ---- a: (., [a-b) @1=poi UPDATED) b: (., [b-c) @2=yaya, @1=poi UPDATED) c: (., [c-d) @2=yaya UPDATED) # Add a new range key to the batch. The batch contains 3 internal range keys, # and the skiplist of fragmented range keys contains 3 elements. mutate range-key-set e f @3 foo ---- # Refreshing the iterator's view of the batch through SetOptions should surface # the new range key. An earlier bug incorrectly compared the number of # fragmented range keys to the number of internal batch range keys in order to # determine when to refresh the iterator. iter iter=i1 first next next set-options first next next next seek-ge bat ---- a: (., [a-b) @1=poi UPDATED) b: (., [b-c) @2=yaya, @1=poi UPDATED) c: (., [c-d) @2=yaya UPDATED) . a: (., [a-b) @1=poi UPDATED) b: (., [b-c) @2=yaya, @1=poi UPDATED) c: (., [c-d) @2=yaya UPDATED) e: (., [e-f) @3=foo UPDATED) bat: (., [b-c) @2=yaya, @1=poi UPDATED) # Mutate the range key under the interleaving iterator's current position in the # indexed batch. # # The last `seek-ge` operation landed on the range key [b-c). The top-level # *pebble.Iterator needs to step the iterator again to see if there's a # coincident point key at (`bat`), which would've advanced the interleaving # iterator to the range key with bounds [c,d), so the underlying interleaving # iterator is positioned ahead at: # # c: (., [c-d) @2=yaya) # # If we call set-options to refresh the iterator's view of the indexed batch, # the range-key-unset [c,d)@2 becomes visible, and the range key that the # underlying interleaving iterator is positioned over should not be visible. # # A bug previously allowed this range key to be visible when seeking into this # span's bounds (see the optimization in InterleavingIter.SeekGE). Now, the call # to SetOptions clears the interleaving iterator's positional state to avoid the # SeekGE optimization. mutate range-key-unset b d @2 ---- iter iter=i1 set-options seek-ge cat ---- . e: (., [e-f) @3=foo UPDATED) reset ---- batch range-key-set a e @1 foo ---- flush ---- new-batch ---- new-batch-iter batchiter ---- new-db-iter dbiter ---- # Test RangeKeyChanged() semantics. # Seeking to the same prefix returns RangeKeyChanged()=false. # Seeking to a new prefix returns RangeKeyChanged()=true. # Seeking to the same prefix with a SetOptions call in between returns # RangeKeyChanged()=true. iter iter=dbiter seek-prefix-ge b@3 seek-prefix-ge b@4 seek-prefix-ge c@3 seek-prefix-ge d@3 set-options seek-prefix-ge d@1 ---- b@3: (., [b-"b\x00") @1=foo UPDATED) b@4: (., [b-"b\x00") @1=foo) c@3: (., [c-"c\x00") @1=foo UPDATED) d@3: (., [d-"d\x00") @1=foo UPDATED) . d@1: (., [d-"d\x00") @1=foo UPDATED) # Test the same semantics on a batch iterator. iter iter=batchiter seek-prefix-ge b@3 seek-prefix-ge b@4 seek-prefix-ge c@3 seek-prefix-ge d@3 set-options seek-prefix-ge d@1 ---- b@3: (., [b-"b\x00") @1=foo UPDATED) b@4: (., [b-"b\x00") @1=foo) c@3: (., [c-"c\x00") @1=foo UPDATED) d@3: (., [d-"d\x00") @1=foo UPDATED) . d@1: (., [d-"d\x00") @1=foo UPDATED) # Test mutating the indexed batch's range keys, overlapping the existing seek # position. It should not see the new mutations, but after a call to SetOptions # it should AND it should return RangeKeyChanged()=true. mutate range-key-set d e @2 foo ---- iter iter=batchiter seek-prefix-ge d@2 set-options seek-prefix-ge d@2 ---- d@2: (., [d-"d\x00") @1=foo) . d@2: (., [d-"d\x00") @2=foo, @1=foo UPDATED) # Test cloning an iterator with a range-key mask block property filter # configured. If the cloned and the clonee iterators have different suffixes # configured, their suffixes should be respected. Previously, the # RangeKeyMasking.Filter option was a footgun, because it was a single mutable # instance. Cloning the iterator without supplying new iterator options would # result in two iterators using the same filter. reset ---- batch range-key-set a e @5 foo set b@4 b@4 ---- new-db-iter iter-a ---- iter iter=iter-a set-options mask-suffix=@3 mask-filter=true seek-ge a next next ---- . a: (., [a-e) @5=foo UPDATED) b@4: (b@4, [a-e) @5=foo) . clone from=iter-a to=iter-b refresh-batch=false ---- iter iter=iter-b set-options mask-suffix=@6 seek-ge a next ---- . a: (., [a-e) @5=foo UPDATED) . iter iter=iter-a seek-ge a next next ---- a: (., [a-e) @5=foo UPDATED) b@4: (b@4, [a-e) @5=foo) .