@@ -11,6 +11,7 @@ import (
11
11
"github.com/google/btree"
12
12
13
13
"github.com/ava-labs/avalanchego/ids"
14
+ "github.com/ava-labs/avalanchego/utils/buffer"
14
15
"github.com/ava-labs/avalanchego/utils/maybe"
15
16
)
16
17
@@ -22,17 +23,18 @@ var (
22
23
// stores previous trie states
23
24
type trieHistory struct {
24
25
// Root ID --> The most recent change resulting in [rootID].
25
- lastChanges map [ids.ID ]* changeSummaryAndIndex
26
+ lastChanges map [ids.ID ]* changeSummaryAndInsertNumber
26
27
27
28
// Maximum number of previous roots/changes to store in [history].
28
29
maxHistoryLen int
29
30
30
31
// Contains the history.
31
32
// Sorted by increasing order of insertion.
32
33
// Contains at most [maxHistoryLen] values.
33
- history * btree. BTreeG [ * changeSummaryAndIndex ]
34
+ history buffer. Deque [ * changeSummaryAndInsertNumber ]
34
35
35
- nextIndex uint64
36
+ // Each change is tagged with this monotonic increasing number.
37
+ nextInsertNumber uint64
36
38
}
37
39
38
40
// Tracks the beginning and ending state of a value.
@@ -43,11 +45,11 @@ type change[T any] struct {
43
45
44
46
// Wrapper around a changeSummary that allows comparison
45
47
// of when the change was made.
46
- type changeSummaryAndIndex struct {
48
+ type changeSummaryAndInsertNumber struct {
47
49
* changeSummary
48
- // Another changeSummaryAndIndex with a greater
49
- // [index ] means that change was after this one.
50
- index uint64
50
+ // Another changeSummaryAndInsertNumber with a greater
51
+ // [insertNumber ] means that change was after this one.
52
+ insertNumber uint64
51
53
}
52
54
53
55
// Tracks all of the node and value changes that resulted in the rootID.
@@ -67,13 +69,8 @@ func newChangeSummary(estimatedSize int) *changeSummary {
67
69
func newTrieHistory (maxHistoryLookback int ) * trieHistory {
68
70
return & trieHistory {
69
71
maxHistoryLen : maxHistoryLookback ,
70
- history : btree .NewG (
71
- 2 ,
72
- func (a , b * changeSummaryAndIndex ) bool {
73
- return a .index < b .index
74
- },
75
- ),
76
- lastChanges : make (map [ids.ID ]* changeSummaryAndIndex ),
72
+ history : buffer.NewUnboundedDeque [* changeSummaryAndInsertNumber ](maxHistoryLookback ),
73
+ lastChanges : make (map [ids.ID ]* changeSummaryAndInsertNumber ),
77
74
}
78
75
}
79
76
@@ -88,39 +85,54 @@ func (th *trieHistory) getValueChanges(startRoot, endRoot ids.ID, start []byte,
88
85
return newChangeSummary (maxLength ), nil
89
86
}
90
87
91
- // Confirm there's a change resulting in [startRoot] before
92
- // a change resulting in [endRoot] in the history.
93
- // [lastEndRootChange] is the last change in the history resulting in [endRoot].
94
- lastEndRootChange , ok := th .lastChanges [endRoot ]
88
+ // [endRootChanges] is the last change in the history resulting in [endRoot].
89
+ endRootChanges , ok := th .lastChanges [endRoot ]
95
90
if ! ok {
96
91
return nil , ErrRootIDNotPresent
97
92
}
98
93
99
- // [startRootChanges] is the last appearance of [startRoot]
94
+ // Confirm there's a change resulting in [startRoot] before
95
+ // a change resulting in [endRoot] in the history.
96
+ // [startRootChanges] is the last appearance of [startRoot].
100
97
startRootChanges , ok := th .lastChanges [startRoot ]
101
98
if ! ok {
102
99
return nil , ErrStartRootNotFound
103
100
}
104
101
105
- // startRootChanges is after the lastEndRootChange, but that is just the latest appearance of start root
106
- // there may be an earlier entry, so attempt to find an entry that comes before lastEndRootChange
107
- if startRootChanges .index > lastEndRootChange .index {
108
- th .history .DescendLessOrEqual (
109
- lastEndRootChange ,
110
- func (item * changeSummaryAndIndex ) bool {
111
- if item == lastEndRootChange {
112
- return true // Skip first iteration
113
- }
114
- if item .rootID == startRoot {
115
- startRootChanges = item
116
- return false
117
- }
118
- return true
119
- },
120
- )
121
- // There's no change resulting in [startRoot] before the latest change resulting in [endRoot].
122
- if startRootChanges .index > lastEndRootChange .index {
123
- return nil , ErrStartRootNotFound
102
+ var (
103
+ // The insert number of the last element in [th.history].
104
+ mostRecentChangeInsertNumber = th .nextInsertNumber - 1
105
+
106
+ // The index within [th.history] of its last element.
107
+ mostRecentChangeIndex = th .history .Len () - 1
108
+
109
+ // The difference between the last index in [th.history] and the index of [endRootChanges].
110
+ endToMostRecentOffset = int (mostRecentChangeInsertNumber - endRootChanges .insertNumber )
111
+
112
+ // The index in [th.history] of the latest change resulting in [endRoot].
113
+ endRootIndex = mostRecentChangeIndex - endToMostRecentOffset
114
+ )
115
+
116
+ if startRootChanges .insertNumber > endRootChanges .insertNumber {
117
+ // [startRootChanges] happened after [endRootChanges].
118
+ // However, that is just the *latest* change resulting in [startRoot].
119
+ // Attempt to find a change resulting in [startRoot] before [endRootChanges].
120
+ //
121
+ // Translate the insert number to the index in [th.history] so we can iterate
122
+ // backward from [endRootChanges].
123
+ for i := endRootIndex - 1 ; i >= 0 ; i -- {
124
+ changes , _ := th .history .Index (i )
125
+
126
+ if changes .rootID == startRoot {
127
+ // [startRootChanges] is now the last change resulting in
128
+ // [startRoot] before [endRootChanges].
129
+ startRootChanges = changes
130
+ break
131
+ }
132
+
133
+ if i == 0 {
134
+ return nil , ErrStartRootNotFound
135
+ }
124
136
}
125
137
}
126
138
@@ -132,58 +144,56 @@ func (th *trieHistory) getValueChanges(startRoot, endRoot ids.ID, start []byte,
132
144
},
133
145
)
134
146
135
- startPath := newPath (start )
136
- endPath := maybe .Bind (end , newPath )
147
+ var (
148
+ startPath = newPath (start )
149
+ endPath = maybe .Bind (end , newPath )
150
+
151
+ // For each element in the history in the range between [startRoot]'s
152
+ // last appearance (exclusive) and [endRoot]'s last appearance (inclusive),
153
+ // add the changes to keys in [start, end] to [combinedChanges].
154
+ // Only the key-value pairs with the greatest [maxLength] keys will be kept.
155
+ combinedChanges = newChangeSummary (maxLength )
156
+
157
+ // The difference between the index of [startRootChanges] and [endRootChanges] in [th.history].
158
+ startToEndOffset = int (endRootChanges .insertNumber - startRootChanges .insertNumber )
137
159
138
- // For each element in the history in the range between [startRoot]'s
139
- // last appearance (exclusive) and [endRoot]'s last appearance (inclusive),
140
- // add the changes to keys in [start, end] to [combinedChanges].
141
- // Only the key-value pairs with the greatest [maxLength] keys will be kept.
142
- combinedChanges := newChangeSummary (maxLength )
160
+ // The index of the last change resulting in [startRoot]
161
+ // which occurs before [endRootChanges].
162
+ startRootIndex = endRootIndex - startToEndOffset
163
+ )
143
164
144
165
// For each change after [startRootChanges] up to and including
145
- // [lastEndRootChange], record the change in [combinedChanges].
146
- th .history .AscendGreaterOrEqual (
147
- startRootChanges ,
148
- func (item * changeSummaryAndIndex ) bool {
149
- if item == startRootChanges {
150
- // Start from the first change after [startRootChanges].
151
- return true
152
- }
153
- if item .index > lastEndRootChange .index {
154
- // Don't go past [lastEndRootChange].
155
- return false
166
+ // [endRootChanges], record the change in [combinedChanges].
167
+ for i := startRootIndex + 1 ; i <= endRootIndex ; i ++ {
168
+ changes , _ := th .history .Index (i )
169
+
170
+ // Add the changes from this commit to [combinedChanges].
171
+ for key , valueChange := range changes .values {
172
+ // The key is outside the range [start, end].
173
+ if (len (startPath ) > 0 && key .Compare (startPath ) < 0 ) ||
174
+ (end .HasValue () && key .Compare (endPath .Value ()) > 0 ) {
175
+ continue
156
176
}
157
177
158
- // Add the changes from this commit to [combinedChanges].
159
- for key , valueChange := range item .values {
160
- // The key is outside the range [start, end].
161
- if (len (startPath ) > 0 && key .Compare (startPath ) < 0 ) ||
162
- (end .HasValue () && key .Compare (endPath .Value ()) > 0 ) {
163
- continue
178
+ // A change to this key already exists in [combinedChanges]
179
+ // so update its before value with the earlier before value
180
+ if existing , ok := combinedChanges .values [key ]; ok {
181
+ existing .after = valueChange .after
182
+ if existing .before .HasValue () == existing .after .HasValue () &&
183
+ bytes .Equal (existing .before .Value (), existing .after .Value ()) {
184
+ // The change to this key is a no-op, so remove it from [combinedChanges].
185
+ delete (combinedChanges .values , key )
186
+ sortedKeys .Delete (key )
164
187
}
165
-
166
- // A change to this key already exists in [combinedChanges]
167
- // so update its before value with the earlier before value
168
- if existing , ok := combinedChanges .values [key ]; ok {
169
- existing .after = valueChange .after
170
- if existing .before .HasValue () == existing .after .HasValue () &&
171
- bytes .Equal (existing .before .Value (), existing .after .Value ()) {
172
- // The change to this key is a no-op, so remove it from [combinedChanges].
173
- delete (combinedChanges .values , key )
174
- sortedKeys .Delete (key )
175
- }
176
- } else {
177
- combinedChanges .values [key ] = & change [maybe.Maybe [[]byte ]]{
178
- before : valueChange .before ,
179
- after : valueChange .after ,
180
- }
181
- sortedKeys .ReplaceOrInsert (key )
188
+ } else {
189
+ combinedChanges .values [key ] = & change [maybe.Maybe [[]byte ]]{
190
+ before : valueChange .before ,
191
+ after : valueChange .after ,
182
192
}
193
+ sortedKeys .ReplaceOrInsert (key )
183
194
}
184
- // continue to next change list
185
- return true
186
- })
195
+ }
196
+ }
187
197
188
198
// Keep only the smallest [maxLength] items in [combinedChanges.values].
189
199
for sortedKeys .Len () > maxLength {
@@ -207,41 +217,42 @@ func (th *trieHistory) getChangesToGetToRoot(rootID ids.ID, start []byte, end ma
207
217
}
208
218
209
219
var (
210
- startPath = newPath (start )
211
- endPath = maybe .Bind (end , newPath )
212
- combinedChanges = newChangeSummary (defaultPreallocationSize )
220
+ startPath = newPath (start )
221
+ endPath = maybe .Bind (end , newPath )
222
+ combinedChanges = newChangeSummary (defaultPreallocationSize )
223
+ mostRecentChangeInsertNumber = th .nextInsertNumber - 1
224
+ mostRecentChangeIndex = th .history .Len () - 1
225
+ offset = int (mostRecentChangeInsertNumber - lastRootChange .insertNumber )
226
+ lastRootChangeIndex = mostRecentChangeIndex - offset
213
227
)
214
228
215
229
// Go backward from the most recent change in the history up to but
216
230
// not including the last change resulting in [rootID].
217
231
// Record each change in [combinedChanges].
218
- th .history .Descend (
219
- func (item * changeSummaryAndIndex ) bool {
220
- if item == lastRootChange {
221
- return false
222
- }
223
- for key , changedNode := range item .nodes {
224
- combinedChanges .nodes [key ] = & change [* node ]{
225
- after : changedNode .before ,
226
- }
232
+ for i := mostRecentChangeIndex ; i > lastRootChangeIndex ; i -- {
233
+ changes , _ := th .history .Index (i )
234
+
235
+ for key , changedNode := range changes .nodes {
236
+ combinedChanges .nodes [key ] = & change [* node ]{
237
+ after : changedNode .before ,
227
238
}
239
+ }
228
240
229
- for key , valueChange := range item .values {
230
- if (len (startPath ) == 0 || key .Compare (startPath ) >= 0 ) &&
231
- (endPath .IsNothing () || key .Compare (endPath .Value ()) <= 0 ) {
232
- if existing , ok := combinedChanges .values [key ]; ok {
233
- existing .after = valueChange .before
234
- } else {
235
- combinedChanges .values [key ] = & change [maybe.Maybe [[]byte ]]{
236
- before : valueChange .after ,
237
- after : valueChange .before ,
238
- }
241
+ for key , valueChange := range changes .values {
242
+ if (len (startPath ) == 0 || key .Compare (startPath ) >= 0 ) &&
243
+ (endPath .IsNothing () || key .Compare (endPath .Value ()) <= 0 ) {
244
+ if existing , ok := combinedChanges .values [key ]; ok {
245
+ existing .after = valueChange .before
246
+ } else {
247
+ combinedChanges .values [key ] = & change [maybe.Maybe [[]byte ]]{
248
+ before : valueChange .after ,
249
+ after : valueChange .before ,
239
250
}
240
251
}
241
252
}
242
- return true
243
- },
244
- )
253
+ }
254
+ }
255
+
245
256
return combinedChanges , nil
246
257
}
247
258
@@ -252,25 +263,27 @@ func (th *trieHistory) record(changes *changeSummary) {
252
263
return
253
264
}
254
265
255
- for th .history .Len () == th .maxHistoryLen {
266
+ if th .history .Len () == th .maxHistoryLen {
256
267
// This change causes us to go over our lookback limit.
257
268
// Remove the oldest set of changes.
258
- oldestEntry , _ := th .history .DeleteMin ()
269
+ oldestEntry , _ := th .history .PopLeft ()
270
+
259
271
latestChange := th .lastChanges [oldestEntry .rootID ]
260
272
if latestChange == oldestEntry {
261
273
// The removed change was the most recent resulting in this root ID.
262
274
delete (th .lastChanges , oldestEntry .rootID )
263
275
}
264
276
}
265
277
266
- changesAndIndex := & changeSummaryAndIndex {
278
+ changesAndIndex := & changeSummaryAndInsertNumber {
267
279
changeSummary : changes ,
268
- index : th .nextIndex ,
280
+ insertNumber : th .nextInsertNumber ,
269
281
}
270
- th .nextIndex ++
282
+ th .nextInsertNumber ++
271
283
272
284
// Add [changes] to the sorted change list.
273
- _ , _ = th .history .ReplaceOrInsert (changesAndIndex )
285
+ _ = th .history .PushRight (changesAndIndex )
286
+
274
287
// Mark that this is the most recent change resulting in [changes.rootID].
275
288
th .lastChanges [changes .rootID ] = changesAndIndex
276
289
}
0 commit comments