# Test a scenario that should NOT result in defragmentation. define a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} c-d:{(#4,RANGEKEYSET,@3,bananas)} d-e:{(#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} ---- iter first next next last prev prev ---- a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} c-d:{(#4,RANGEKEYSET,@3,bananas)} d-e:{(#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} d-e:{(#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} c-d:{(#4,RANGEKEYSET,@3,bananas)} a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} iter first next next next last prev prev prev ---- a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} c-d:{(#4,RANGEKEYSET,@3,bananas)} d-e:{(#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} d-e:{(#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} c-d:{(#4,RANGEKEYSET,@3,bananas)} a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} # Test a scenario that SHOULD result in internal defragmentation ([a,c) and # [c,d) should be merged. define a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} c-d:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} d-e:{(#1,RANGEKEYSET,@3,bananas)} ---- iter first next next ---- a-d:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} d-e:{(#1,RANGEKEYSET,@3,bananas)} # Test defragmenting in both directions at seek keys. define a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} f-h:{(#3,RANGEKEYSET,@3,bananas)} h-p:{(#3,RANGEKEYSET,@3,bananas)} p-t:{(#3,RANGEKEYSET,@3,bananas)} ---- iter seekge b prev seekge b next seeklt d next seeklt d prev ---- a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} iter seeklt d next prev ---- a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} # Test next-ing and prev-ing around seek keys. define a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} f-h:{(#3,RANGEKEYSET,@3,bananas)} h-p:{(#3,RANGEKEYSET,@3,bananas)} p-t:{(#3,RANGEKEYSET,@3,bananas)} t-z:{(#4,RANGEKEYSET,@2,oranges)} ---- iter seekge r prev next next ---- f-t:{(#3,RANGEKEYSET,@3,bananas)} a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} t-z:{(#4,RANGEKEYSET,@2,oranges)} iter seekge f seekge h seekge p seekge t seekge u seekge v seekge z ---- f-t:{(#3,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} t-z:{(#4,RANGEKEYSET,@2,oranges)} t-z:{(#4,RANGEKEYSET,@2,oranges)} t-z:{(#4,RANGEKEYSET,@2,oranges)} iter seeklt f seeklt h seeklt p seeklt t seeklt u seeklt z ---- a-f:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} f-t:{(#3,RANGEKEYSET,@3,bananas)} t-z:{(#4,RANGEKEYSET,@2,oranges)} t-z:{(#4,RANGEKEYSET,@2,oranges)} # Test iteration with a reducer that collects keys across all spans that # constitute a defragmented span. Abutting spans are always combined. define a-b:{(#3,RANGEDEL) (#2,RANGEDEL)} b-c:{(#4,RANGEDEL) (#1,RANGEDEL)} c-d:{(#5,RANGEDEL)} e-f:{(#1,RANGEDEL)} f-g:{(#2,RANGEDEL)} ---- iter equal=always reducer=collect first next next last prev prev ---- a-d:{(#5,RANGEDEL) (#4,RANGEDEL) (#3,RANGEDEL) (#2,RANGEDEL) (#1,RANGEDEL)} e-g:{(#2,RANGEDEL) (#1,RANGEDEL)} e-g:{(#2,RANGEDEL) (#1,RANGEDEL)} a-d:{(#5,RANGEDEL) (#4,RANGEDEL) (#3,RANGEDEL) (#2,RANGEDEL) (#1,RANGEDEL)} # Test defragmentation of non-empty (i.e. more than one value) fragments, while # empty fragments are left untouched. define a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} c-d:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} d-e:{} e-f:{} g-h:{(#1,RANGEKEYSET,@3,bananas)} ---- iter first next next next next ---- a-d:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} d-e:{} e-f:{} g-h:{(#1,RANGEKEYSET,@3,bananas)} iter last prev prev prev prev ---- g-h:{(#1,RANGEKEYSET,@3,bananas)} e-f:{} d-e:{} a-d:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} iter seekge d next prev seekge e next prev prev prev ---- d-e:{} e-f:{} d-e:{} e-f:{} g-h:{(#1,RANGEKEYSET,@3,bananas)} e-f:{} d-e:{} a-d:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} iter seeklt e next prev seeklt f next prev prev prev ---- d-e:{} e-f:{} d-e:{} e-f:{} g-h:{(#1,RANGEKEYSET,@3,bananas)} e-f:{} d-e:{} a-d:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} # Test that the defragmenting iterator does yield errors in cases that do not # need to defragment. define a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} c-d:{(#4,RANGEKEYSET,@3,bananas)} d-e:{(#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} ---- iter probes=(ErrInjected) seek-ge b seek-lt d first last ---- err= err= err= err= # Next and Prev may only be called on positioned iterators, so to test # propagation of errors on Next or Prev, we must use a probe that injects errors # on Next or Prev but leaves seeks untouched. # # The situation is complicated by the fact that a seek on the defragmenting # iterator will result in Next/Prevs on the embedded iterator (in order to peek # ahead to see if anything needs to be defragmented). # # First we test the seeks too result in injected errors when they Next/Prev # ahead to determine if there's anything to defragment. iter probes=((If (Or OpNext OpPrev) ErrInjected noop), (Log "# inner.")) seek-ge b next seek-lt cat prev ---- # inner.SeekGE("b") = a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} # inner.Prev() = nil err= # inner.Next() = nil err= # inner.SeekLT("cat") = c-d:{(#4,RANGEKEYSET,@3,bananas)} # inner.Next() = nil err= # inner.Prev() = nil err= # Use a probe that injects errors whenever we otherwise would've returned the # c-d span. First and Last calls should both return errors because during # defragmenting they'll step the internal iterator on to the error position. iter probes=((If (Equal StartKey (Bytes "c")) ErrInjected noop), (Log "# inner.")) first last ---- # inner.First() = a-c:{(#3,RANGEKEYUNSET,@5) (#2,RANGEKEYSET,@5,apples) (#1,RANGEKEYSET,@3,bananas)} # inner.Next() = nil err= # inner.Last() = d-e:{(#4,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} # inner.Prev() = nil err= # In order to test that errors are injected when Next-ing the top-level # iterator, define test data that includes 5 spans. define a-b:{(#3,RANGEKEYUNSET,@5)} b-c:{(#4,RANGEKEYSET,@5,apples)} c-d:{(#5,RANGEKEYSET,@3,bananas)} d-e:{(#6,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} e-f:{(#4,RANGEKEYSET,@1,pineapple)} ---- # Use a probe that injects errors whenever we would've otherwise returned the # c-d span. Our initial First/Last seeks should not step on to the error # position and should not error. The subsequent Next/Prev however should. iter probes=((If (Equal StartKey (Bytes "c")) ErrInjected noop), (Log "# inner.")) first next last prev ---- # inner.First() = a-b:{(#3,RANGEKEYUNSET,@5)} # inner.Next() = b-c:{(#4,RANGEKEYSET,@5,apples)} a-b:{(#3,RANGEKEYUNSET,@5)} # inner.Next() = nil err= # inner.Last() = e-f:{(#4,RANGEKEYSET,@1,pineapple)} # inner.Prev() = d-e:{(#6,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} e-f:{(#4,RANGEKEYSET,@1,pineapple)} # inner.Prev() = nil err= # When seeking, the defragmenting iterator needs to defragment in both # directions. A forward seek first defragments in the reverse direction, and # then in the forward direction. A backward seek does the inverse. If an error # is encountered while performing the first defragment scan, it must be # surfaced. # # To test this scenario we again inject errors instead of the c-d span. # - The SeekGE('d') should land on d-e, try to defragment backward first and # encounter the error. # - The SeekLT('c') should land on b-c, try to defragment forward first and # encounter the error. iter probes=((If (Equal StartKey (Bytes "c")) ErrInjected noop), (Log "# inner.")) seek-ge d seek-lt c ---- # inner.SeekGE("d") = d-e:{(#6,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} # inner.Prev() = nil err= # inner.SeekLT("c") = b-c:{(#4,RANGEKEYSET,@5,apples)} # inner.Next() = nil err= # When changing directions in some circumstances we step an iterator and then # defragment twice; once to skip over the current span and once to construct the # next defragmented span in the new iteration direction. If the first step of # the iterator surfaces an error, ensure that it's still propagated. iter probes=((If (And OpPrev (Equal StartKey (Bytes "c"))) ErrInjected noop), (Log "# inner.")) seek-ge c prev ---- # inner.SeekGE("c") = c-d:{(#5,RANGEKEYSET,@3,bananas)} # inner.Prev() = b-c:{(#4,RANGEKEYSET,@5,apples)} # inner.Next() = c-d:{(#5,RANGEKEYSET,@3,bananas)} # inner.Next() = d-e:{(#6,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} c-d:{(#5,RANGEKEYSET,@3,bananas)} # inner.Prev() = nil err= iter probes=((If (And OpNext (Equal StartKey (Bytes "c"))) ErrInjected noop), (Log "# inner.")) seek-lt d next ---- # inner.SeekLT("d") = c-d:{(#5,RANGEKEYSET,@3,bananas)} # inner.Next() = d-e:{(#6,RANGEKEYSET,@3,bananas) (#4,RANGEKEYSET,@1,pineapple)} # inner.Prev() = c-d:{(#5,RANGEKEYSET,@3,bananas)} # inner.Prev() = b-c:{(#4,RANGEKEYSET,@5,apples)} c-d:{(#5,RANGEKEYSET,@3,bananas)} # inner.Next() = nil err=