11package iavl
22
33import (
4+ "math/rand"
45 "sort"
56 "testing"
67
@@ -32,6 +33,56 @@ func TestIterator_NewIterator_NilTree_Failure(t *testing.T) {
3233 performTest (t , itr )
3334 require .ErrorIs (t , errFastIteratorNilNdbGiven , itr .Error ())
3435 })
36+
37+ t .Run ("Unsaved Fast Iterator" , func (t * testing.T ) {
38+ itr := NewUnsavedFastIterator (start , end , ascending , nil , map [string ]* FastNode {}, map [string ]interface {}{})
39+ performTest (t , itr )
40+ require .ErrorIs (t , errFastIteratorNilNdbGiven , itr .Error ())
41+ })
42+ }
43+
44+ func TestUnsavedFastIterator_NewIterator_NilAdditions_Failure (t * testing.T ) {
45+ var start , end []byte = []byte {'a' }, []byte {'c' }
46+ ascending := true
47+
48+ performTest := func (t * testing.T , itr dbm.Iterator ) {
49+ require .NotNil (t , itr )
50+ require .False (t , itr .Valid ())
51+ actualsStart , actualEnd := itr .Domain ()
52+ require .Equal (t , start , actualsStart )
53+ require .Equal (t , end , actualEnd )
54+ require .Error (t , itr .Error ())
55+ }
56+
57+ t .Run ("Nil additions given" , func (t * testing.T ) {
58+ tree , err := NewMutableTree (dbm .NewMemDB (), 0 )
59+ require .NoError (t , err )
60+ itr := NewUnsavedFastIterator (start , end , ascending , tree .ndb , nil , tree .unsavedFastNodeRemovals )
61+ performTest (t , itr )
62+ require .ErrorIs (t , errUnsavedFastIteratorNilAdditionsGiven , itr .Error ())
63+ })
64+
65+ t .Run ("Nil removals given" , func (t * testing.T ) {
66+ tree , err := NewMutableTree (dbm .NewMemDB (), 0 )
67+ require .NoError (t , err )
68+ itr := NewUnsavedFastIterator (start , end , ascending , tree .ndb , tree .unsavedFastNodeAdditions , nil )
69+ performTest (t , itr )
70+ require .ErrorIs (t , errUnsavedFastIteratorNilRemovalsGiven , itr .Error ())
71+ })
72+
73+ t .Run ("All nil" , func (t * testing.T ) {
74+ itr := NewUnsavedFastIterator (start , end , ascending , nil , nil , nil )
75+ performTest (t , itr )
76+ require .ErrorIs (t , errFastIteratorNilNdbGiven , itr .Error ())
77+ })
78+
79+ t .Run ("Additions and removals are nil" , func (t * testing.T ) {
80+ tree , err := NewMutableTree (dbm .NewMemDB (), 0 )
81+ require .NoError (t , err )
82+ itr := NewUnsavedFastIterator (start , end , ascending , tree .ndb , nil , nil )
83+ performTest (t , itr )
84+ require .ErrorIs (t , errUnsavedFastIteratorNilAdditionsGiven , itr .Error ())
85+ })
3586}
3687
3788func TestIterator_Empty_Invalid (t * testing.T ) {
@@ -57,6 +108,11 @@ func TestIterator_Empty_Invalid(t *testing.T) {
57108 itr , mirror := setupFastIteratorAndMirror (t , config )
58109 performTest (t , itr , mirror )
59110 })
111+
112+ t .Run ("Unsaved Fast Iterator" , func (t * testing.T ) {
113+ itr , mirror := setupUnsavedFastIterator (t , config )
114+ performTest (t , itr , mirror )
115+ })
60116}
61117
62118func TestIterator_Basic_Ranged_Ascending_Success (t * testing.T ) {
@@ -89,6 +145,12 @@ func TestIterator_Basic_Ranged_Ascending_Success(t *testing.T) {
89145 require .True (t , itr .Valid ())
90146 performTest (t , itr , mirror )
91147 })
148+
149+ t .Run ("Unsaved Fast Iterator" , func (t * testing.T ) {
150+ itr , mirror := setupUnsavedFastIterator (t , config )
151+ require .True (t , itr .Valid ())
152+ performTest (t , itr , mirror )
153+ })
92154}
93155
94156func TestIterator_Basic_Ranged_Descending_Success (t * testing.T ) {
@@ -121,6 +183,12 @@ func TestIterator_Basic_Ranged_Descending_Success(t *testing.T) {
121183 require .True (t , itr .Valid ())
122184 performTest (t , itr , mirror )
123185 })
186+
187+ t .Run ("Unsaved Fast Iterator" , func (t * testing.T ) {
188+ itr , mirror := setupUnsavedFastIterator (t , config )
189+ require .True (t , itr .Valid ())
190+ performTest (t , itr , mirror )
191+ })
124192}
125193
126194func TestIterator_Basic_Full_Ascending_Success (t * testing.T ) {
@@ -133,9 +201,6 @@ func TestIterator_Basic_Full_Ascending_Success(t *testing.T) {
133201 }
134202
135203 performTest := func (t * testing.T , itr dbm.Iterator , mirror [][]string ) {
136-
137- require .Equal (t , 25 , len (mirror ))
138-
139204 actualStart , actualEnd := itr .Domain ()
140205 require .Equal (t , config .startIterate , actualStart )
141206 require .Equal (t , config .endIterate , actualEnd )
@@ -148,12 +213,21 @@ func TestIterator_Basic_Full_Ascending_Success(t *testing.T) {
148213 t .Run ("Iterator" , func (t * testing.T ) {
149214 itr , mirror := setupIteratorAndMirror (t , config )
150215 require .True (t , itr .Valid ())
216+ require .Equal (t , 25 , len (mirror ))
151217 performTest (t , itr , mirror )
152218 })
153219
154220 t .Run ("Fast Iterator" , func (t * testing.T ) {
155221 itr , mirror := setupFastIteratorAndMirror (t , config )
156222 require .True (t , itr .Valid ())
223+ require .Equal (t , 25 , len (mirror ))
224+ performTest (t , itr , mirror )
225+ })
226+
227+ t .Run ("Unsaved Fast Iterator" , func (t * testing.T ) {
228+ itr , mirror := setupUnsavedFastIterator (t , config )
229+ require .True (t , itr .Valid ())
230+ require .Equal (t , 25 - 25 / 4 + 1 , len (mirror )) // to account for removals
157231 performTest (t , itr , mirror )
158232 })
159233}
@@ -168,8 +242,6 @@ func TestIterator_Basic_Full_Descending_Success(t *testing.T) {
168242 }
169243
170244 performTest := func (t * testing.T , itr dbm.Iterator , mirror [][]string ) {
171- require .Equal (t , 25 , len (mirror ))
172-
173245 actualStart , actualEnd := itr .Domain ()
174246 require .Equal (t , config .startIterate , actualStart )
175247 require .Equal (t , config .endIterate , actualEnd )
@@ -181,12 +253,21 @@ func TestIterator_Basic_Full_Descending_Success(t *testing.T) {
181253
182254 t .Run ("Iterator" , func (t * testing.T ) {
183255 itr , mirror := setupIteratorAndMirror (t , config )
256+ require .Equal (t , 25 , len (mirror ))
184257 require .True (t , itr .Valid ())
185258 performTest (t , itr , mirror )
186259 })
187260
188261 t .Run ("Fast Iterator" , func (t * testing.T ) {
189262 itr , mirror := setupFastIteratorAndMirror (t , config )
263+ require .Equal (t , 25 , len (mirror ))
264+ require .True (t , itr .Valid ())
265+ performTest (t , itr , mirror )
266+ })
267+
268+ t .Run ("Unsaved Fast Iterator" , func (t * testing.T ) {
269+ itr , mirror := setupUnsavedFastIterator (t , config )
270+ require .Equal (t , 25 - 25 / 4 + 1 , len (mirror )) // to account for removals
190271 require .True (t , itr .Valid ())
191272 performTest (t , itr , mirror )
192273 })
@@ -238,13 +319,21 @@ func TestIterator_WithDelete_Full_Ascending_Success(t *testing.T) {
238319 require .True (t , itr .Valid ())
239320 assertIterator (t , itr , sortedMirror , config .ascending )
240321 })
322+
323+ t .Run ("Unsaved Fast Iterator" , func (t * testing.T ) {
324+ itr := NewUnsavedFastIterator (config .startIterate , config .endIterate , config .ascending , immutableTree .ndb , tree .unsavedFastNodeAdditions , tree .unsavedFastNodeRemovals )
325+ require .True (t , itr .Valid ())
326+ assertIterator (t , itr , sortedMirror , config .ascending )
327+ })
241328}
242329
243330func setupIteratorAndMirror (t * testing.T , config * iteratorTestConfig ) (dbm.Iterator , [][]string ) {
244331 tree , err := NewMutableTree (dbm .NewMemDB (), 0 )
245332 require .NoError (t , err )
246333
247334 mirror := setupMirrorForIterator (t , config , tree )
335+ _ , _ , err = tree .SaveVersion ()
336+ require .NoError (t , err )
248337
249338 immutableTree , err := tree .GetImmutable (tree .ndb .getLatestVersion ())
250339 require .NoError (t , err )
@@ -258,7 +347,63 @@ func setupFastIteratorAndMirror(t *testing.T, config *iteratorTestConfig) (dbm.I
258347 require .NoError (t , err )
259348
260349 mirror := setupMirrorForIterator (t , config , tree )
350+ _ , _ , err = tree .SaveVersion ()
351+ require .NoError (t , err )
261352
262353 itr := NewFastIterator (config .startIterate , config .endIterate , config .ascending , tree .ndb )
263354 return itr , mirror
264355}
356+
357+ func setupUnsavedFastIterator (t * testing.T , config * iteratorTestConfig ) (dbm.Iterator , [][]string ) {
358+ tree , err := NewMutableTree (dbm .NewMemDB (), 0 )
359+ require .NoError (t , err )
360+
361+ // For unsaved fast iterator, we would like to test the state where
362+ // there are saved fast nodes as well as some unsaved additions and removals.
363+ // So, we split the byte range in half where the first half is saved and the second half is unsaved.
364+ breakpointByte := (config .endByteToSet + config .startByteToSet ) / 2
365+
366+ firstHalfConfig := * config
367+ firstHalfConfig .endByteToSet = breakpointByte // exclusive
368+
369+ secondHalfConfig := * config
370+ secondHalfConfig .startByteToSet = breakpointByte
371+
372+ firstHalfMirror := setupMirrorForIterator (t , & firstHalfConfig , tree )
373+ _ , _ , err = tree .SaveVersion ()
374+ require .NoError (t , err )
375+
376+ // No unsaved additions or removals should be present after saving
377+ require .Equal (t , 0 , len (tree .unsavedFastNodeAdditions ))
378+ require .Equal (t , 0 , len (tree .unsavedFastNodeRemovals ))
379+
380+ // Ensure that there are unsaved additions and removals present
381+ secondHalfMirror := setupMirrorForIterator (t , & secondHalfConfig , tree )
382+
383+ require .True (t , len (tree .unsavedFastNodeAdditions ) >= len (secondHalfMirror ))
384+ require .Equal (t , 0 , len (tree .unsavedFastNodeRemovals ))
385+
386+ // Merge the two halves
387+ var mergedMirror [][]string
388+ if config .ascending {
389+ mergedMirror = append (firstHalfMirror , secondHalfMirror ... )
390+ } else {
391+ mergedMirror = append (secondHalfMirror , firstHalfMirror ... )
392+ }
393+
394+ if len (mergedMirror ) > 0 {
395+ // Remove random keys
396+ for i := 0 ; i < len (mergedMirror ) / 4 ; i ++ {
397+ randIndex := rand .Intn (len (mergedMirror ))
398+ keyToRemove := mergedMirror [randIndex ][0 ]
399+
400+ _ , removed := tree .Remove ([]byte (keyToRemove ))
401+ require .True (t , removed )
402+
403+ mergedMirror = append (mergedMirror [:randIndex ], mergedMirror [randIndex + 1 :]... )
404+ }
405+ }
406+
407+ itr := NewUnsavedFastIterator (config .startIterate , config .endIterate , config .ascending , tree .ndb , tree .unsavedFastNodeAdditions , tree .unsavedFastNodeRemovals )
408+ return itr , mergedMirror
409+ }
0 commit comments