@@ -407,8 +407,15 @@ func (db *Database) Commit(node common.Hash, report bool) error {
407
407
// There's no data to commit in this node
408
408
return nil
409
409
}
410
- db .lock .Lock ()
411
- defer db .lock .Unlock ()
410
+ // acquire read lock only, as commit doesn't modify dirties
411
+ // that allows concurrent node reads e.g. performed by rpc handlers
412
+ db .lock .RLock ()
413
+ rLocked := true
414
+ defer func () {
415
+ if rLocked {
416
+ db .lock .RUnlock ()
417
+ }
418
+ }()
412
419
413
420
// Create a database batch to flush persistent data out. It is important that
414
421
// outside code doesn't see an inconsistent state (referenced data removed from
@@ -418,9 +425,7 @@ func (db *Database) Commit(node common.Hash, report bool) error {
418
425
batch := db .diskdb .NewBatch ()
419
426
420
427
// Move the trie itself into the batch, flushing if enough data is accumulated
421
- nodes , storage := len (db .dirties ), db .dirtiesSize
422
-
423
- uncacher := & cleaner {db }
428
+ uncacher := newDelayedCleaner (db )
424
429
if err := db .commit (node , batch , uncacher ); err != nil {
425
430
log .Error ("Failed to commit trie from trie database" , "err" , err )
426
431
return err
@@ -436,6 +441,18 @@ func (db *Database) Commit(node common.Hash, report bool) error {
436
441
}
437
442
batch .Reset ()
438
443
444
+ // prevent RUnlock in defer and RUnlock the lock
445
+ rLocked = false
446
+ db .lock .RUnlock ()
447
+
448
+ // acquire write lock for the uncacher to remove commited nodes from dirties
449
+ db .lock .Lock ()
450
+ defer db .lock .Unlock ()
451
+
452
+ nodes , storage := len (db .dirties ), db .dirtiesSize
453
+
454
+ uncacher .Clean ()
455
+
439
456
// Reset the storage counters and bumped metrics
440
457
memcacheCommitTimeTimer .Update (time .Since (start ))
441
458
memcacheCommitBytesMeter .Mark (int64 (storage - db .dirtiesSize ))
@@ -456,7 +473,8 @@ func (db *Database) Commit(node common.Hash, report bool) error {
456
473
}
457
474
458
475
// commit is the private locked version of Commit.
459
- func (db * Database ) commit (hash common.Hash , batch ethdb.Batch , uncacher * cleaner ) error {
476
+ // note: commit must not modify dirties as it should only require read lock held
477
+ func (db * Database ) commit (hash common.Hash , batch ethdb.Batch , uncacher * delayedCleaner ) error {
460
478
// If the node does not exist, it's a previously committed node
461
479
node , ok := db .dirties [hash ]
462
480
if ! ok {
0 commit comments