define a.SET.1:b ---- iter first next ---- a#1,1:b . define a.SET.2:c a.SET.1:b ---- iter first next ---- a#2,1:c . iter snapshots=0 first next ---- a#2,1:c . iter snapshots=1 first next ---- a#2,1:c . iter snapshots=2 first next next ---- a#2,1:c a#1,1:b . define a.DEL.2: a.SET.1:b ---- iter first next ---- a#2,0: . iter elide-tombstones=true first ---- . iter elide-tombstones=true snapshots=2 first next next ---- a#2,0: a#1,1:b . iter elide-tombstones=true snapshots=1 first next ---- a#2,0: . define a.DEL.2: a.SET.1:b b.SET.3:c ---- iter first next next ---- a#2,0: b#3,1:c . iter snapshots=1 first next next ---- a#2,0: b#3,1:c . iter snapshots=2 first next next next ---- a#2,0: a#1,1:b b#3,1:c . define a.SET.1:a b.SET.2:b c.SET.3:c ---- iter first next next next ---- a#1,1:a b#2,1:b c#3,1:c . define a.MERGE.3:d a.MERGE.2:c a.SET.1:b b.MERGE.2:b b.MERGE.1:a ---- iter first next next ---- a#3,1:bcd[base] b#2,2:ab . iter snapshots=3 print-snapshot-pinned print-force-obsolete first next next next ---- a#3,2:d (not pinned) (not force obsolete) a#2,1:bc[base] (pinned) (not force obsolete) b#2,2:ab (not pinned) (not force obsolete) . define a.SET.9:b a.DEL.8: a.SET.7:d a.DEL.6: a.SET.5:f ---- iter first next ---- a#9,18:b . iter snapshots=6 first next next ---- a#9,18:b a#5,1:f . iter snapshots=7 first next next ---- a#9,18:b a#6,0: . iter snapshots=8 first next next ---- a#9,18:b a#7,18:d . iter snapshots=9 first next next ---- a#9,1:b a#8,0: . iter snapshots=10 first next ---- a#9,18:b . iter snapshots=(5,6,7,8,9) first next next next next next ---- a#9,1:b a#8,0: a#7,1:d a#6,0: a#5,1:f . define a.INVALID.2:b a.SET.1:c ---- iter first ---- err=invalid internal key kind: INVALID define a.SET.2:b a.INVALID.1:c ---- iter first next ---- a#2,18:b err=invalid internal key kind: INVALID define a.MERGE.2:b a.INVALID.1:c ---- iter first next ---- a#2,2:b err=invalid internal key kind: INVALID define a.INVALID.2:c a.RANGEDEL.1:d ---- iter first tombstones ---- err=invalid internal key kind: INVALID . define a.MERGE.2:b a.MERGE.1:c a.MERGE.0:d ---- iter snapshots=(1,2) print-snapshot-pinned print-force-obsolete first next next next ---- a#2,2:b (not pinned) (not force obsolete) a#1,2:c (pinned) (not force obsolete) a#0,2:d (pinned) (not force obsolete) . define a.SET.2:b a.RANGEDEL.1:c b.RANGEDEL.4:d b.SET.2:e c.SET.3:f ---- iter first next next next tombstones ---- a#2,18:b a#1,15:c b#4,15:d . a-b#1 b-c#4 c-d#4 . iter snapshots=2 first next next next tombstones ---- a#2,1:b a#1,15:c b#4,15:d . a-b#1 b-c#4 b-c#1 c-d#4 . iter snapshots=3 print-snapshot-pinned print-force-obsolete first next next next next tombstones ---- a#2,18:b (not pinned) (not force obsolete) a#1,15:c (not pinned) (not force obsolete) b#4,15:d (not pinned) (not force obsolete) b#2,1:e (pinned) (force obsolete) . a-b#1 b-c#4 b-c#1 c-d#4 . iter snapshots=4 print-snapshot-pinned print-force-obsolete first next next next next next tombstones ---- a#2,18:b (not pinned) (not force obsolete) a#1,15:c (not pinned) (not force obsolete) b#4,15:d (not pinned) (not force obsolete) b#2,1:e (pinned) (force obsolete) c#3,1:f (pinned) (force obsolete) . a-b#1 b-c#4 b-c#1 c-d#4 . define a.RANGEDEL.3:e b.SET.4:b c.SET.3:c d.SET.2:d e.SET.1:e ---- iter first next next next next tombstones ---- a#3,15:e b#4,1:b c#3,1:c e#1,1:e . a-e#3 . define a.RANGEDEL.3:e b.MERGE.4:b c.MERGE.3:c d.MERGE.2:d e.MERGE.1:e ---- iter first next next next next tombstones ---- a#3,15:e b#4,2:b c#3,2:c e#1,2:e . a-e#3 . define a.RANGEDEL.3:c b.MERGE.5:e b.MERGE.4:d b.MERGE.2:c b.MERGE.1:b d.MERGE.5:c d.MERGE.4:b d.RANGEDEL.3:f d.MERGE.2:e d.MERGE.1:d ---- iter first next next next next tombstones ---- a#3,15:c b#5,1:de[base] d#5,2:bc d#3,15:f . a-c#3 d-f#3 . define a.RANGEDEL.3:d b.RANGEDEL.2:e c.RANGEDEL.1:f ---- iter first next next next tombstones ---- a#3,15:d b#2,15:e c#1,15:f . a-b#3 b-c#3 c-d#3 d-e#2 e-f#1 . iter snapshots=2 first next next next tombstones ---- a#3,15:d b#2,15:e c#1,15:f . a-b#3 b-c#3 c-d#3 c-d#1 d-e#2 d-e#1 e-f#1 . iter snapshots=3 first next next next tombstones ---- a#3,15:d b#2,15:e c#1,15:f . a-b#3 b-c#3 b-c#2 c-d#3 c-d#2 d-e#2 e-f#1 . iter snapshots=(2,3) first next next next tombstones ---- a#3,15:d b#2,15:e c#1,15:f . a-b#3 b-c#3 b-c#2 c-d#3 c-d#2 c-d#1 d-e#2 d-e#1 e-f#1 . define a.RANGEDEL.10:k f.SET.9:f f.SET.8:f ---- iter snapshots=(9,10) print-snapshot-pinned print-force-obsolete first next tombstones f next tombstones ---- a#10,15:k (not pinned) (not force obsolete) f#9,1:f (pinned) (force obsolete) a-f#10 . f#8,1:f (pinned) (force obsolete) f-k#10 . define f.RANGEDEL.10:k f.SET.9:f f.SET.8:f ---- iter snapshots=(9,10) first next tombstones f next tombstones ---- f#10,15:k f#9,1:f . f#8,1:f f-k#10 . define a.SET.1:a b.RANGEDEL.2:d c.RANGEDEL.3:e d.SET.4:d ---- iter first next next next tombstones c tombstones ---- a#1,1:a b#2,15:d c#3,15:e d#4,1:d b-c#2 . c-d#3 d-e#3 . iter snapshots=3 first next next next tombstones c tombstones ---- a#1,1:a b#2,15:d c#3,15:e d#4,1:d b-c#2 . c-d#3 c-d#2 d-e#3 . define a.SET.1:a b.RANGEDEL.2:d c.SET.4:d ---- iter first next next tombstones c tombstones ---- a#1,1:a b#2,15:d c#4,1:d b-c#2 . c-d#2 . define a.RANGEDEL.2:d a.SET.2:a b.SET.2:b c.SET.2:c ---- iter first next next next next ---- a#2,15:d a#2,1:a b#2,1:b c#2,1:c . define a.SINGLEDEL.1: ---- iter first next ---- a#1,7: . iter elide-tombstones=true first ---- . define a.SINGLEDEL.2: a.SINGLEDEL.1: ---- iter first next ---- a#2,7: . define a.SINGLEDEL.3: a.SINGLEDEL.2: a.SET.1:a ---- iter first ---- . define a.SET.3:a b.SINGLEDEL.2: b.DEL.1: ---- iter first next next ---- a#3,1:a b#2,0: . define a.SINGLEDEL.2: a.DEL.1: ---- iter first next ---- a#2,0: . iter elide-tombstones=true first ---- . define a.SINGLEDEL.2: a.MERGE.1: ---- iter first next ---- a#2,0: . iter elide-tombstones=true first ---- . define a.SINGLEDEL.2: a.SET.1:b ---- iter first ---- . # SET that meets a SINGLEDEL is transformed into a SETWITHDEL. define a.SET.2:b a.SINGLEDEL.1: ---- iter first next ---- a#2,18:b . define a.MERGE.6:b a.SINGLEDEL.5: a.SET.4:a ---- iter first next ---- a#6,18:b[base] . # Non-deterministic use of SINGLEDEL where there are two older SETs that have # not been deleted or single deleted. It is permitted to shadow both. define a.MERGE.6:b a.SINGLEDEL.5: a.SET.4:a a.SET.3:a ---- iter first next ---- a#6,18:b[base] . define a.SINGLEDEL.2: a.SET.1:b b.SET.3:c ---- iter first next ---- b#3,1:c . define a.SINGLEDEL.3: a.SET.2:b a.SET.1:a ---- iter first next ---- a#1,1:a . define a.SINGLEDEL.3: a.MERGE.2:b a.MERGE.1:a ---- iter first next ---- a#3,0: . define a.SINGLEDEL.4: a.SET.3:val a.SINGLEDEL.2: a.SET.1:val ---- iter first ---- . iter snapshots=2 first next next ---- a#2,7: a#1,1:val . define a.SINGLEDEL.4: a.SET.3:val a.DEL.2: a.SET.1:val ---- iter first next ---- a#2,0: . iter snapshots=2 first next next ---- a#2,0: a#1,1:val . iter snapshots=3 first next ---- a#2,0: . iter snapshots=(2,3) first next next ---- a#2,0: a#1,1:val . define a.SINGLEDEL.4: a.SET.3:c a.MERGE.2:b a.SET.1:a ---- iter first next ---- a#2,1:ab[base] . iter snapshots=2 first next next ---- a#2,2:b a#1,1:a . iter snapshots=3 first next ---- a#2,1:ab[base] . iter snapshots=(2,3,4) first next next next next ---- a#4,7: a#3,1:c a#2,2:b a#1,1:a . define a.SINGLEDEL.3: a.RANGEDEL.2:c a.SET.1:val ---- iter first next next tombstones ---- a#3,7: a#2,15:c . a-c#2 . define a.RANGEDEL.3:d a.DEL.2: a.SET.1:a d.DEL.2: ---- iter first next next tombstones ---- a#3,15:d d#2,0: . a-d#3 . iter snapshots=3 first next next next ---- a#3,15:d a#2,0: d#2,0: . iter snapshots=2 first next next next ---- a#3,15:d a#1,1:a d#2,0: . iter snapshots=1 first next next ---- a#3,15:d d#2,0: . define a.MERGE.2:a b.RANGEDEL.1:c ---- iter first tombstones a next next tombstones ---- a#2,2:a . b#1,15:c . b-c#1 . define a.MERGE.2:v2 a.RANGEDEL.1:b a.MERGE.1:v1 ---- iter allow-zero-seqnum=true first next next next tombstones ---- a#2,2:v2 a#1,15:b a#0,2:v1 . a-b#1 . # Verify that we transform merge+del -> set. define a.MERGE.5:5 a.DEL.3: a.MERGE.1:1 ---- iter first next ---- a#5,18:5[base] . iter allow-zero-seqnum=true first next ---- a#0,18:5[base] . iter elide-tombstones=true first next ---- a#5,18:5[base] . iter snapshots=2 first next next ---- a#5,18:5[base] a#1,2:1 . iter snapshots=2 elide-tombstones=true first next next ---- a#5,18:5[base] a#1,2:1 . # Verify that we transform merge+rangedel -> set. This isn't strictly # necessary, but provides consistency with the behavior for merge+del. define a.RANGEDEL.3:c b.MERGE.5:5 b.SET.2:2 b.MERGE.1:1 ---- iter first next next ---- a#3,15:c b#5,1:5[base] . iter allow-zero-seqnum=true first next next ---- a#3,15:c b#0,1:5[base] . iter snapshots=2 first next next ---- a#3,15:c b#5,1:5[base] b#1,2:1 define a.RANGEDEL.3:c b.MERGE.5:5 b.MERGE.2:2 b.MERGE.1:1 ---- iter first next next ---- a#3,15:c b#5,1:5[base] . iter snapshots=2 first next next ---- a#3,15:c b#5,1:5[base] b#1,2:1 # SET that meets a DEL is transformed into a SETWITHDEL. define a.SET.2:b a.DEL.1: ---- iter first next ---- a#2,18:b . iter snapshots=2 first next next ---- a#2,1:b a#1,0: . define a.SET.3:c a.DEL.2: a.SET.1:b ---- iter first next ---- a#3,18:c . iter snapshots=2 first next next ---- a#3,18:c a#1,1:b . define a.SET.3:c a.SET.2:b a.DEL.1: ---- iter first next ---- a#3,18:c . iter snapshots=3 first next next ---- a#3,1:c a#2,18:b . iter snapshots=2 first next next ---- a#3,1:c a#1,0: . define a.DEL.3: a.SET.2:b a.DEL.1: ---- iter first next ---- a#3,0: . iter snapshots=3 first next next ---- a#3,0: a#2,18:b . iter snapshots=2 first next next ---- a#3,0: a#1,0: . # SETWITHDEL-eligible entries at or under a RANGEDEL at the same user key should # be skipped. define a.SET.3:c a.RANGEDEL.2:z a.SET.2:b a.DEL.1: ---- iter allow-zero-seqnum=true first next next ---- a#0,18:c a#2,15:z . iter allow-zero-seqnum=true snapshots=3 first next next next ---- a#3,1:c a#2,15:z a#0,18:b . iter allow-zero-seqnum=true snapshots=2 first next next next ---- a#3,18:c a#2,15:z a#1,0: . define a.SET.4:c a.RANGEDEL.3:z a.SET.2:b a.DEL.1: ---- iter first next next ---- a#4,18:c a#3,15:z . # Invalid keys are emitted under SETWITHDEL. define a.SET.2:b a.INVALID.1: ---- iter first next ---- a#2,18:b err=invalid internal key kind: INVALID define a.SET.3:c a.INVALID.2: a.SET.1:b ---- iter first next ---- a#3,18:c err=invalid internal key kind: INVALID # SINGLEDEL that meets a SETWITHDEL is transformed into a DEL. define a.SINGLEDEL.3: a.SETWITHDEL.2:d b.SET.1:c ---- iter first next next ---- a#3,0: b#1,1:c . iter snapshots=2 first next next ---- a#3,0: b#1,1:c . iter snapshots=3 first next next next ---- a#3,7: a#2,18:d b#1,1:c . define a.SETWITHDEL.3:3 a.SET.2:d b.SET.1:c ---- iter print-missized-dels first next next ---- a#3,18:3 b#1,1:c . missized-dels=0 iter snapshots=3 first next next next ---- a#3,18:3 a#2,1:d b#1,1:c . # Test a DELSIZED whose encoded value matches the size of a deleted key. The # DELSIZED's value should be removed, reflecting that the tombstone already # dropped the key that it was expected to drop. define a.SET.9:foo b.DELSIZED.8:varint(11) b.SET.5:helloworld c.SET.2:bar ---- iter print-missized-dels first next next ---- a#9,1:foo b#8,23: c#2,1:bar missized-dels=0 # Test two DELSIZEDs meeting. The lower-sequenced number value should carry # forward, at the higher sequence number. The first DELSIZED should be consider # missized: It never found the key it was supposed to delete. define a.SET.9:foo b.DELSIZED.9:varint(20) b.DELSIZED.8:varint(10) c.SET.2:bar ---- iter print-missized-dels first next next ---- a#9,1:foo b#9,23:varint(10) c#2,1:bar missized-dels=1 # Test a DELSIZED whose encoded value is larger than the size of the deleted # key. The DELSIZED should be replaced by an ordinary DEL with the same sequence # number. define a.SET.2:foo b.DELSIZED.8:varint(25) b.SET.3:hello c.SET.9:bar ---- iter print-missized-dels first next next ---- a#2,1:foo b#8,0: c#9,1:bar missized-dels=1 # Test two DELSIZED at the same user key, but with correctly sized deleted keys. define a.DELSIZED.9:varint(4) a.SET.8:foo a.DELSIZED.8:varint(6) a.SET.5:hello ---- iter print-missized-dels first next ---- a#9,23: . missized-dels=0 # Test the above scenario, except the second DELSIZED is missized. It should # still count as missized. define a.DELSIZED.9:varint(4) a.SET.8:foo a.DELSIZED.8:varint(1) a.SET.5:hello ---- iter print-missized-dels first next ---- a#9,0: . missized-dels=1 # Test the above scenario, except the second tombstone is a DEL. It should # NOT count as missized. define a.DELSIZED.9:varint(4) a.SET.8:foo a.DEL.8: a.SET.5:hello ---- iter print-missized-dels first next ---- a#9,0: . missized-dels=0 # Test various DELSIZEDs beneath live keys. SETS should be converted to # SETWITHDELs when they meet a DELSIZED. define a.SET.7:foo a.DELSIZED.5:varint(5) b.SET.4:bar b.DELSIZED.2:varint(4) b.SET.1:bax c.SET.9:coconut c.DEL.8:del c.DELSIZED.5:varint(2) d.SET.8:dragonfruit ---- iter print-missized-dels first next next next next ---- a#7,18:foo b#4,18:bar c#9,18:coconut d#8,1:dragonfruit . missized-dels=0 # Test a DELSIZED meeting a MERGE. This counts as a missized DEL—The user can't # know the value of the most recent MERGE since it's dependent on LSM state. define a.DELSIZED.9:varint(4) a.MERGE.8:fo a.MERGE.7:o ---- iter print-missized-dels first next ---- a#9,0: . missized-dels=1 # Test a DELSIZED that shadows a SINGLEDEL'd key. define a.DELSIZED.4:varint(4) b.SINGLEDEL.3: b.SET.1:val ---- iter first next tombstones ---- a#4,23:varint(4) . . # Repeat the above but with elision of tombstones. iter elide-tombstones=t first tombstones ---- . . # Test DELSIZED shadowing SINGLEDEL. define a.DELSIZED.4:varint(4) a.SET.2:foo b.SINGLEDEL.3: b.SET.1:val ---- iter first next tombstones ---- a#4,23: . . # Repeat the above but with elision of tombstones. iter elide-tombstones=t first tombstones ---- . . # Test a very subtle sequence where a elision of tombstones is active, and a # unskippable RANGEDEL sits between a DELSIZED and the key it was intended to # delete. The unskippable RANGEDEL breaks the skipping of keys within the # snapshot stripe, but it's ultimately okay because we preserve skip=true across # the RANGEDEL return. define a.DELSIZED.5:varint(4) a.RANGEDEL.4:d a.SET.3:foo ---- iter elide-tombstones=t first next tombstones ---- a#4,15:d . . # Try the same test as above, but with allowing sequence number zeroing as well. iter elide-tombstones=t allow-zero-seqnum=t first next tombstones ---- a#4,15:d . . # Perform a variant of the above test but with a DEL key. define a.DEL.5: a.RANGEDEL.4:d a.SET.3:foo ---- iter elide-tombstones=t first next tombstones ---- a#4,15:d . . # Perform a variant of the above test but with a SINGLEDEL key. define a.SINGLEDEL.5: a.RANGEDEL.4:d a.SET.3:foo ---- iter elide-tombstones=t first next tombstones ---- a#4,15:d . . # Perform a few variants of the above but with a range del with a seqnum equal to # keys. NB: When seqnums are equal, the order of keys with various kinds is: # # DeleteSized < RangeKey{Delete,Unset,Set} < SetWithDelete < RangeDelete < SingleDelete < Set < Delete # # NB: Range keys are interleaved always at the maximal sequence number, so the # compaction iterator should always observe them first. define a.SINGLEDEL.6: a.SETWITHDEL.5:foo a.RANGEDEL.5:z ---- define-range-keys a-z:{(#5,RANGEKEYDEL)} ---- # In the following case, the SINGLEDEL meets a SETWITHDEL, promoting the # SINGLEDEL into a DEL. iter first next next next tombstones ---- a#72057594037927935,19: a#6,0: a#5,15:z . a-z#5 . # In this case, SINGLEDEL is elided (despite its transformation into a DEL) due # to elide-tombstones=t. iter elide-tombstones=t first next next tombstones ---- a#72057594037927935,19: a#5,15:z . . define a.SINGLEDEL.6: a.RANGEDEL.5:d a.SET.5:foo ---- # NB: In this case, the RANGEDEL acts as an unintentional snapshot stripe # change. This is a code artifact, and we will be able to remove this behavior # when range deletes are interleaved at the maximal sequence number by an # interleaving iterator (like range keys are). iter first next next next tombstones ---- a#6,7: a#5,15:d a#5,1:foo . a-d#5 . iter elide-tombstones=t allow-zero-seqnum=t first next tombstones ---- a#5,15:d . . define a.SINGLEDEL.6: a.SETWITHDEL.5:foo a.RANGEDEL.5:d ---- # When the SINGLEDEL and SETWITHDEL meet, the SINGLEDEL is promoted into a DEL. iter first next tombstones ---- a#6,0: a#5,15:d a-d#5 . iter elide-tombstones=t first next tombstones ---- a#5,15:d . . define a.DELSIZED.6:varint(3) a.RANGEDEL.5:d a.SET.5:foo ---- iter first next tombstones ---- a#6,23:varint(3) a#5,15:d a-d#5 . iter elide-tombstones=t first next tombstones ---- a#5,15:d . . # Test a DELSIZED with a value that fails to decode. define a.DELSIZED.5:notavarint a.SET.4:foo ---- iter first ---- err=DELSIZED holds invalid value: 6e6f7461766172696e74 # Test a value-less DELSIZED. define a.DELSIZED.5: a.SET.4:foo a.SET.3:bar ---- iter print-missized-dels first next ---- a#5,0: . missized-dels=0 # Regression test for #3087. # # When a DELSIZED and a SINGLEDEL meet in a compaction, a DEL key should be # emitted. define a.DELSIZED.5: a.SINGLEDEL.3: a.SET.2:foo a.SET.1:bar ---- iter first next ---- a#5,0: . # When a MERGE and a DEL[SIZED] meet in a compaction, a SETWITHDEL key (NOT a # SET) should be emitted. Otherwise, a sequence such as SINGLEDDEL, MERGE, DEL, # SET could result in the SET re-appearing. define a.MERGE.5:foo a.DEL.3: ---- iter first next ---- a#5,18:foo[base] .