Skip to content

Commit e2d35e9

Browse files
dboehm-avalabsDan Laine
andauthored
Firewoodize merkle db Part 1: Make Views ReadOnly (#1816)
Co-authored-by: Dan Laine <daniel.laine@avalabs.org>
1 parent 0b68899 commit e2d35e9

File tree

10 files changed

+435
-812
lines changed

10 files changed

+435
-812
lines changed

x/merkledb/db.go

Lines changed: 100 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -236,27 +236,22 @@ func (db *merkleDB) rebuild(ctx context.Context) error {
236236
it := db.nodeDB.NewIterator()
237237
defer it.Release()
238238

239-
currentViewSize := 0
240239
viewSizeLimit := math.Max(
241240
db.nodeCache.maxSize/rebuildViewSizeFractionOfCacheSize,
242241
minRebuildViewSizePerCommit,
243242
)
244-
245-
currentView, err := db.newUntrackedView(viewSizeLimit)
246-
if err != nil {
247-
return err
248-
}
243+
currentOps := make([]database.BatchOp, 0, viewSizeLimit)
249244

250245
for it.Next() {
251-
if currentViewSize >= viewSizeLimit {
252-
if err := currentView.commitToDB(ctx); err != nil {
246+
if len(currentOps) >= viewSizeLimit {
247+
view, err := db.newUntrackedView(currentOps)
248+
if err != nil {
253249
return err
254250
}
255-
currentView, err = db.newUntrackedView(viewSizeLimit)
256-
if err != nil {
251+
if err := view.commitToDB(ctx); err != nil {
257252
return err
258253
}
259-
currentViewSize = 0
254+
currentOps = make([]database.BatchOp, 0, viewSizeLimit)
260255
}
261256

262257
key := it.Key()
@@ -266,20 +261,21 @@ func (db *merkleDB) rebuild(ctx context.Context) error {
266261
if err != nil {
267262
return err
268263
}
269-
if n.hasValue() {
270-
serializedPath := path.Serialize()
271-
if err := currentView.Insert(ctx, serializedPath.Value, n.value.Value()); err != nil {
272-
return err
273-
}
274-
currentViewSize++
275-
} else if err := db.nodeDB.Delete(key); err != nil {
276-
return err
277-
}
264+
265+
currentOps = append(currentOps, database.BatchOp{
266+
Key: path.Serialize().Value,
267+
Value: n.value.Value(),
268+
Delete: !n.hasValue(),
269+
})
278270
}
279271
if err := it.Error(); err != nil {
280272
return err
281273
}
282-
if err := currentView.commitToDB(ctx); err != nil {
274+
view, err := db.newUntrackedView(currentOps)
275+
if err != nil {
276+
return err
277+
}
278+
if err := view.commitToDB(ctx); err != nil {
283279
return err
284280
}
285281
return db.nodeDB.Compact(nil, nil)
@@ -301,8 +297,16 @@ func (db *merkleDB) CommitChangeProof(ctx context.Context, proof *ChangeProof) e
301297
if db.closed {
302298
return database.ErrClosed
303299
}
300+
ops := make([]database.BatchOp, len(proof.KeyChanges))
301+
for i, kv := range proof.KeyChanges {
302+
ops[i] = database.BatchOp{
303+
Key: kv.Key,
304+
Value: kv.Value.Value(),
305+
Delete: kv.Value.IsNothing(),
306+
}
307+
}
304308

305-
view, err := db.prepareChangeProofView(proof)
309+
view, err := db.newUntrackedView(ops)
306310
if err != nil {
307311
return err
308312
}
@@ -317,10 +321,37 @@ func (db *merkleDB) CommitRangeProof(ctx context.Context, start []byte, proof *R
317321
return database.ErrClosed
318322
}
319323

320-
view, err := db.prepareRangeProofView(start, proof)
324+
ops := make([]database.BatchOp, len(proof.KeyValues))
325+
keys := set.NewSet[string](len(proof.KeyValues))
326+
for i, kv := range proof.KeyValues {
327+
keys.Add(string(kv.Key))
328+
ops[i] = database.BatchOp{
329+
Key: kv.Key,
330+
Value: kv.Value,
331+
}
332+
}
333+
334+
var largestKey []byte
335+
if len(proof.KeyValues) > 0 {
336+
largestKey = proof.KeyValues[len(proof.KeyValues)-1].Key
337+
}
338+
keysToDelete, err := db.getKeysNotInSet(start, largestKey, keys)
339+
if err != nil {
340+
return err
341+
}
342+
for _, keyToDelete := range keysToDelete {
343+
ops = append(ops, database.BatchOp{
344+
Key: keyToDelete,
345+
Delete: true,
346+
})
347+
}
348+
349+
// Don't need to lock [view] because nobody else has a reference to it.
350+
view, err := db.newUntrackedView(ops)
321351
if err != nil {
322352
return err
323353
}
354+
324355
return view.commitToDB(ctx)
325356
}
326357

@@ -469,7 +500,7 @@ func (db *merkleDB) getProof(ctx context.Context, key []byte) (*Proof, error) {
469500
return nil, database.ErrClosed
470501
}
471502

472-
view, err := db.newUntrackedView(defaultPreallocationSize)
503+
view, err := db.newUntrackedView(nil)
473504
if err != nil {
474505
return nil, err
475506
}
@@ -633,35 +664,37 @@ func (db *merkleDB) GetChangeProof(
633664

634665
// NewView returns a new view on top of this trie.
635666
// Changes made to the view will only be reflected in the original trie if Commit is called.
636-
// Assumes [db.lock] isn't held.
637-
func (db *merkleDB) NewView() (TrieView, error) {
638-
return db.NewPreallocatedView(defaultPreallocationSize)
639-
}
667+
// Assumes [db.commitLock] and [db.lock] aren't held.
668+
func (db *merkleDB) NewView(batchOps []database.BatchOp) (TrieView, error) {
669+
// ensure the db doesn't change while creating the new view
670+
db.commitLock.RLock()
671+
defer db.commitLock.RUnlock()
640672

641-
// Returns a new view that isn't tracked in [db.childViews].
642-
// For internal use only, namely in methods that create short-lived views.
643-
// Assumes [db.lock] and/or [db.commitLock] is read locked.
644-
func (db *merkleDB) newUntrackedView(estimatedSize int) (*trieView, error) {
645-
return newTrieView(db, db, db.root.clone(), estimatedSize)
646-
}
673+
newView, err := db.newUntrackedView(batchOps)
674+
if err != nil {
675+
return nil, err
676+
}
647677

648-
// NewPreallocatedView returns a new view with memory allocated to hold at least [estimatedSize] value changes at a time.
649-
// If more changes are made, additional memory will be allocated.
650-
// The returned view is added to [db.childViews].
651-
// Assumes [db.lock] isn't held.
652-
func (db *merkleDB) NewPreallocatedView(estimatedSize int) (TrieView, error) {
678+
// ensure access to childViews is protected
653679
db.lock.Lock()
654680
defer db.lock.Unlock()
655681

682+
db.childViews = append(db.childViews, newView)
683+
return newView, nil
684+
}
685+
686+
// Returns a new view that isn't tracked in [db.childViews].
687+
// For internal use only, namely in methods that create short-lived views.
688+
// Assumes [db.lock] isn't held and [db.commitLock] is read locked.
689+
func (db *merkleDB) newUntrackedView(batchOps []database.BatchOp) (*trieView, error) {
656690
if db.closed {
657691
return nil, database.ErrClosed
658692
}
659693

660-
newView, err := newTrieView(db, db, db.root.clone(), estimatedSize)
694+
newView, err := newTrieView(db, db, db.root.clone(), batchOps)
661695
if err != nil {
662696
return nil, err
663697
}
664-
db.childViews = append(db.childViews, newView)
665698
return newView, nil
666699
}
667700

@@ -698,14 +731,15 @@ func (db *merkleDB) Insert(ctx context.Context, k, v []byte) error {
698731
return database.ErrClosed
699732
}
700733

701-
view, err := db.newUntrackedView(defaultPreallocationSize)
734+
view, err := db.newUntrackedView([]database.BatchOp{
735+
{
736+
Key: k,
737+
Value: v,
738+
},
739+
})
702740
if err != nil {
703741
return err
704742
}
705-
// Don't need to lock [view] because nobody else has a reference to it.
706-
if err := view.insert(k, v); err != nil {
707-
return err
708-
}
709743
return view.commitToDB(ctx)
710744
}
711745

@@ -817,14 +851,15 @@ func (db *merkleDB) Remove(ctx context.Context, key []byte) error {
817851
return database.ErrClosed
818852
}
819853

820-
view, err := db.newUntrackedView(defaultPreallocationSize)
854+
view, err := db.newUntrackedView([]database.BatchOp{
855+
{
856+
Key: key,
857+
Delete: true,
858+
},
859+
})
821860
if err != nil {
822861
return err
823862
}
824-
// Don't need to lock [view] because nobody else has a reference to it.
825-
if err = view.remove(key); err != nil {
826-
return err
827-
}
828863
return view.commitToDB(ctx)
829864
}
830865

@@ -836,7 +871,7 @@ func (db *merkleDB) commitBatch(ops []database.BatchOp) error {
836871
return database.ErrClosed
837872
}
838873

839-
view, err := db.prepareBatchView(ops)
874+
view, err := db.newUntrackedView(ops)
840875
if err != nil {
841876
return err
842877
}
@@ -1049,23 +1084,22 @@ func (db *merkleDB) VerifyChangeProof(
10491084
return err
10501085
}
10511086

1087+
// Insert the key-value pairs into the trie.
1088+
ops := make([]database.BatchOp, len(proof.KeyChanges))
1089+
for i, kv := range proof.KeyChanges {
1090+
ops[i] = database.BatchOp{
1091+
Key: kv.Key,
1092+
Value: kv.Value.Value(),
1093+
Delete: kv.Value.IsNothing(),
1094+
}
1095+
}
1096+
10521097
// Don't need to lock [view] because nobody else has a reference to it.
1053-
view, err := db.newUntrackedView(len(proof.KeyChanges))
1098+
view, err := db.newUntrackedView(ops)
10541099
if err != nil {
10551100
return err
10561101
}
10571102

1058-
// Insert the key-value pairs into the trie.
1059-
for _, kv := range proof.KeyChanges {
1060-
if kv.Value.IsNothing() {
1061-
if err := view.removeFromTrie(newPath(kv.Key)); err != nil {
1062-
return err
1063-
}
1064-
} else if _, err := view.insertIntoTrie(newPath(kv.Key), kv.Value); err != nil {
1065-
return err
1066-
}
1067-
}
1068-
10691103
// For all the nodes along the edges of the proof, insert the children whose
10701104
// keys are less than [insertChildrenLessThan] or whose keys are greater
10711105
// than [insertChildrenGreaterThan] into the trie so that we get the
@@ -1172,14 +1206,14 @@ func (db *merkleDB) getHistoricalViewForRange(
11721206

11731207
// looking for the trie's current root id, so return the trie unmodified
11741208
if currentRootID == rootID {
1175-
return newTrieView(db, db, db.root.clone(), 100)
1209+
return newTrieView(db, db, db.root.clone(), nil)
11761210
}
11771211

11781212
changeHistory, err := db.history.getChangesToGetToRoot(rootID, start, end)
11791213
if err != nil {
11801214
return nil, err
11811215
}
1182-
return newTrieViewWithChanges(db, db, changeHistory, len(changeHistory.nodes))
1216+
return newTrieViewWithChanges(db, db, changeHistory)
11831217
}
11841218

11851219
// Returns all keys in range [start, end] that aren't in [keySet].
@@ -1261,81 +1295,3 @@ func (db *merkleDB) getNode(key path) (*node, error) {
12611295
err = db.nodeCache.Put(key, node)
12621296
return node, err
12631297
}
1264-
1265-
// Returns a new view atop [db] with the changes in [ops] applied to it.
1266-
// Assumes [db.commitLock] is read locked.
1267-
func (db *merkleDB) prepareBatchView(ops []database.BatchOp) (*trieView, error) {
1268-
view, err := db.newUntrackedView(len(ops))
1269-
if err != nil {
1270-
return nil, err
1271-
}
1272-
// Don't need to lock [view] because nobody else has a reference to it.
1273-
1274-
// write into the trie
1275-
for _, op := range ops {
1276-
if op.Delete {
1277-
if err := view.remove(op.Key); err != nil {
1278-
return nil, err
1279-
}
1280-
} else if err := view.insert(op.Key, op.Value); err != nil {
1281-
return nil, err
1282-
}
1283-
}
1284-
1285-
return view, nil
1286-
}
1287-
1288-
// Returns a new view atop [db] with the key/value pairs in [proof.KeyValues]
1289-
// inserted and the key/value pairs in [proof.DeletedKeys] removed.
1290-
// Assumes [db.commitLock] is locked.
1291-
func (db *merkleDB) prepareChangeProofView(proof *ChangeProof) (*trieView, error) {
1292-
view, err := db.newUntrackedView(len(proof.KeyChanges))
1293-
if err != nil {
1294-
return nil, err
1295-
}
1296-
// Don't need to lock [view] because nobody else has a reference to it.
1297-
1298-
for _, kv := range proof.KeyChanges {
1299-
if kv.Value.IsNothing() {
1300-
if err := view.remove(kv.Key); err != nil {
1301-
return nil, err
1302-
}
1303-
} else if err := view.insert(kv.Key, kv.Value.Value()); err != nil {
1304-
return nil, err
1305-
}
1306-
}
1307-
return view, nil
1308-
}
1309-
1310-
// Returns a new view atop [db] with the key/value pairs in [proof.KeyValues] added and
1311-
// any existing key-value pairs in the proof's range but not in the proof removed.
1312-
// Assumes [db.commitLock] is locked.
1313-
func (db *merkleDB) prepareRangeProofView(start []byte, proof *RangeProof) (*trieView, error) {
1314-
// Don't need to lock [view] because nobody else has a reference to it.
1315-
view, err := db.newUntrackedView(len(proof.KeyValues))
1316-
if err != nil {
1317-
return nil, err
1318-
}
1319-
keys := set.NewSet[string](len(proof.KeyValues))
1320-
for _, kv := range proof.KeyValues {
1321-
keys.Add(string(kv.Key))
1322-
if err := view.insert(kv.Key, kv.Value); err != nil {
1323-
return nil, err
1324-
}
1325-
}
1326-
1327-
var largestKey []byte
1328-
if len(proof.KeyValues) > 0 {
1329-
largestKey = proof.KeyValues[len(proof.KeyValues)-1].Key
1330-
}
1331-
keysToDelete, err := db.getKeysNotInSet(start, largestKey, keys)
1332-
if err != nil {
1333-
return nil, err
1334-
}
1335-
for _, keyToDelete := range keysToDelete {
1336-
if err := view.remove(keyToDelete); err != nil {
1337-
return nil, err
1338-
}
1339-
}
1340-
return view, nil
1341-
}

0 commit comments

Comments
 (0)