github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/trie/database.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package trie 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 "reflect" 24 "runtime" 25 "sync" 26 "time" 27 28 "github.com/VictoriaMetrics/fastcache" 29 30 "github.com/scroll-tech/go-ethereum/common" 31 "github.com/scroll-tech/go-ethereum/core/rawdb" 32 "github.com/scroll-tech/go-ethereum/ethdb" 33 "github.com/scroll-tech/go-ethereum/log" 34 "github.com/scroll-tech/go-ethereum/metrics" 35 "github.com/scroll-tech/go-ethereum/rlp" 36 ) 37 38 var ( 39 memcacheCleanHitMeter = metrics.NewRegisteredMeter("trie/memcache/clean/hit", nil) 40 memcacheCleanMissMeter = metrics.NewRegisteredMeter("trie/memcache/clean/miss", nil) 41 memcacheCleanReadMeter = metrics.NewRegisteredMeter("trie/memcache/clean/read", nil) 42 memcacheCleanWriteMeter = metrics.NewRegisteredMeter("trie/memcache/clean/write", nil) 43 44 memcacheDirtyHitMeter = metrics.NewRegisteredMeter("trie/memcache/dirty/hit", nil) 45 memcacheDirtyMissMeter = metrics.NewRegisteredMeter("trie/memcache/dirty/miss", nil) 46 memcacheDirtyReadMeter = metrics.NewRegisteredMeter("trie/memcache/dirty/read", nil) 47 memcacheDirtyWriteMeter = metrics.NewRegisteredMeter("trie/memcache/dirty/write", nil) 48 49 memcacheFlushTimeTimer = metrics.NewRegisteredResettingTimer("trie/memcache/flush/time", nil) 50 memcacheFlushNodesMeter = metrics.NewRegisteredMeter("trie/memcache/flush/nodes", nil) 51 memcacheFlushSizeMeter = metrics.NewRegisteredMeter("trie/memcache/flush/size", nil) 52 53 memcacheGCTimeTimer = metrics.NewRegisteredResettingTimer("trie/memcache/gc/time", nil) 54 memcacheGCNodesMeter = metrics.NewRegisteredMeter("trie/memcache/gc/nodes", nil) 55 memcacheGCSizeMeter = metrics.NewRegisteredMeter("trie/memcache/gc/size", nil) 56 57 memcacheCommitTimeTimer = metrics.NewRegisteredResettingTimer("trie/memcache/commit/time", nil) 58 memcacheCommitNodesMeter = metrics.NewRegisteredMeter("trie/memcache/commit/nodes", nil) 59 memcacheCommitSizeMeter = metrics.NewRegisteredMeter("trie/memcache/commit/size", nil) 60 ) 61 62 // Database is an intermediate write layer between the trie data structures and 63 // the disk database. The aim is to accumulate trie writes in-memory and only 64 // periodically flush a couple tries to disk, garbage collecting the remainder. 65 // 66 // Note, the trie Database is **not** thread safe in its mutations, but it **is** 67 // thread safe in providing individual, independent node access. The rationale 68 // behind this split design is to provide read access to RPC handlers and sync 69 // servers even while the trie is executing expensive garbage collection. 70 type Database struct { 71 diskdb ethdb.KeyValueStore // Persistent storage for matured trie nodes 72 73 // zktrie related stuff 74 Zktrie bool 75 // TODO: It's a quick&dirty implementation. FIXME later. 76 rawDirties KvMap 77 78 cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs 79 dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes 80 oldest common.Hash // Oldest tracked node, flush-list head 81 newest common.Hash // Newest tracked node, flush-list tail 82 83 gctime time.Duration // Time spent on garbage collection since last commit 84 gcnodes uint64 // Nodes garbage collected since last commit 85 gcsize common.StorageSize // Data storage garbage collected since last commit 86 87 flushtime time.Duration // Time spent on data flushing since last commit 88 flushnodes uint64 // Nodes flushed since last commit 89 flushsize common.StorageSize // Data storage flushed since last commit 90 91 dirtiesSize common.StorageSize // Storage size of the dirty node cache (exc. metadata) 92 childrenSize common.StorageSize // Storage size of the external children tracking 93 preimages *preimageStore // The store for caching preimages 94 95 lock sync.RWMutex 96 } 97 98 // rawNode is a simple binary blob used to differentiate between collapsed trie 99 // nodes and already encoded RLP binary blobs (while at the same time store them 100 // in the same cache fields). 101 type rawNode []byte 102 103 func (n rawNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") } 104 func (n rawNode) fstring(ind string) string { panic("this should never end up in a live trie") } 105 106 func (n rawNode) EncodeRLP(w io.Writer) error { 107 _, err := w.Write(n) 108 return err 109 } 110 111 // rawFullNode represents only the useful data content of a full node, with the 112 // caches and flags stripped out to minimize its data storage. This type honors 113 // the same RLP encoding as the original parent. 114 type rawFullNode [17]node 115 116 func (n rawFullNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") } 117 func (n rawFullNode) fstring(ind string) string { panic("this should never end up in a live trie") } 118 119 func (n rawFullNode) EncodeRLP(w io.Writer) error { 120 var nodes [17]node 121 122 for i, child := range n { 123 if child != nil { 124 nodes[i] = child 125 } else { 126 nodes[i] = nilValueNode 127 } 128 } 129 return rlp.Encode(w, nodes) 130 } 131 132 // rawShortNode represents only the useful data content of a short node, with the 133 // caches and flags stripped out to minimize its data storage. This type honors 134 // the same RLP encoding as the original parent. 135 type rawShortNode struct { 136 Key []byte 137 Val node 138 } 139 140 func (n rawShortNode) cache() (hashNode, bool) { panic("this should never end up in a live trie") } 141 func (n rawShortNode) fstring(ind string) string { panic("this should never end up in a live trie") } 142 143 // cachedNode is all the information we know about a single cached trie node 144 // in the memory database write layer. 145 type cachedNode struct { 146 node node // Cached collapsed trie node, or raw rlp data 147 size uint16 // Byte size of the useful cached data 148 149 parents uint32 // Number of live nodes referencing this one 150 children map[common.Hash]uint16 // External children referenced by this node 151 152 flushPrev common.Hash // Previous node in the flush-list 153 flushNext common.Hash // Next node in the flush-list 154 } 155 156 // cachedNodeSize is the raw size of a cachedNode data structure without any 157 // node data included. It's an approximate size, but should be a lot better 158 // than not counting them. 159 var cachedNodeSize = int(reflect.TypeOf(cachedNode{}).Size()) 160 161 // cachedNodeChildrenSize is the raw size of an initialized but empty external 162 // reference map. 163 const cachedNodeChildrenSize = 48 164 165 // rlp returns the raw rlp encoded blob of the cached trie node, either directly 166 // from the cache, or by regenerating it from the collapsed node. 167 func (n *cachedNode) rlp() []byte { 168 if node, ok := n.node.(rawNode); ok { 169 return node 170 } 171 blob, err := rlp.EncodeToBytes(n.node) 172 if err != nil { 173 panic(err) 174 } 175 return blob 176 } 177 178 // obj returns the decoded and expanded trie node, either directly from the cache, 179 // or by regenerating it from the rlp encoded blob. 180 func (n *cachedNode) obj(hash common.Hash) node { 181 if node, ok := n.node.(rawNode); ok { 182 return mustDecodeNode(hash[:], node) 183 } 184 return expandNode(hash[:], n.node) 185 } 186 187 // forChilds invokes the callback for all the tracked children of this node, 188 // both the implicit ones from inside the node as well as the explicit ones 189 // from outside the node. 190 func (n *cachedNode) forChilds(onChild func(hash common.Hash)) { 191 for child := range n.children { 192 onChild(child) 193 } 194 if _, ok := n.node.(rawNode); !ok { 195 forGatherChildren(n.node, onChild) 196 } 197 } 198 199 // forGatherChildren traverses the node hierarchy of a collapsed storage node and 200 // invokes the callback for all the hashnode children. 201 func forGatherChildren(n node, onChild func(hash common.Hash)) { 202 switch n := n.(type) { 203 case *rawShortNode: 204 forGatherChildren(n.Val, onChild) 205 case rawFullNode: 206 for i := 0; i < 16; i++ { 207 forGatherChildren(n[i], onChild) 208 } 209 case hashNode: 210 onChild(common.BytesToHash(n)) 211 case valueNode, nil, rawNode: 212 default: 213 panic(fmt.Sprintf("unknown node type: %T", n)) 214 } 215 } 216 217 // simplifyNode traverses the hierarchy of an expanded memory node and discards 218 // all the internal caches, returning a node that only contains the raw data. 219 func simplifyNode(n node) node { 220 switch n := n.(type) { 221 case *shortNode: 222 // Short nodes discard the flags and cascade 223 return &rawShortNode{Key: n.Key, Val: simplifyNode(n.Val)} 224 225 case *fullNode: 226 // Full nodes discard the flags and cascade 227 node := rawFullNode(n.Children) 228 for i := 0; i < len(node); i++ { 229 if node[i] != nil { 230 node[i] = simplifyNode(node[i]) 231 } 232 } 233 return node 234 235 case valueNode, hashNode, rawNode: 236 return n 237 238 default: 239 panic(fmt.Sprintf("unknown node type: %T", n)) 240 } 241 } 242 243 // expandNode traverses the node hierarchy of a collapsed storage node and converts 244 // all fields and keys into expanded memory form. 245 func expandNode(hash hashNode, n node) node { 246 switch n := n.(type) { 247 case *rawShortNode: 248 // Short nodes need key and child expansion 249 return &shortNode{ 250 Key: compactToHex(n.Key), 251 Val: expandNode(nil, n.Val), 252 flags: nodeFlag{ 253 hash: hash, 254 }, 255 } 256 257 case rawFullNode: 258 // Full nodes need child expansion 259 node := &fullNode{ 260 flags: nodeFlag{ 261 hash: hash, 262 }, 263 } 264 for i := 0; i < len(node.Children); i++ { 265 if n[i] != nil { 266 node.Children[i] = expandNode(nil, n[i]) 267 } 268 } 269 return node 270 271 case valueNode, hashNode: 272 return n 273 274 default: 275 panic(fmt.Sprintf("unknown node type: %T", n)) 276 } 277 } 278 279 // Config defines all necessary options for database. 280 type Config struct { 281 Cache int // Memory allowance (MB) to use for caching trie nodes in memory 282 Journal string // Journal of clean cache to survive node restarts 283 Preimages bool // Flag whether the preimage of trie key is recorded 284 Zktrie bool // use zktrie 285 } 286 287 // NewDatabase creates a new trie database to store ephemeral trie content before 288 // its written out to disk or garbage collected. No read cache is created, so all 289 // data retrievals will hit the underlying disk database. 290 func NewDatabase(diskdb ethdb.KeyValueStore) *Database { 291 return NewDatabaseWithConfig(diskdb, nil) 292 } 293 294 // NewDatabaseWithConfig creates a new trie database to store ephemeral trie content 295 // before its written out to disk or garbage collected. It also acts as a read cache 296 // for nodes loaded from disk. 297 func NewDatabaseWithConfig(diskdb ethdb.KeyValueStore, config *Config) *Database { 298 var cleans *fastcache.Cache 299 if config != nil && config.Cache > 0 { 300 if config.Journal == "" { 301 cleans = fastcache.New(config.Cache * 1024 * 1024) 302 } else { 303 cleans = fastcache.LoadFromFileOrNew(config.Journal, config.Cache*1024*1024) 304 } 305 } 306 var preimage *preimageStore 307 if config != nil && config.Preimages { 308 preimage = newPreimageStore(diskdb) 309 } 310 db := &Database{ 311 diskdb: diskdb, 312 cleans: cleans, 313 dirties: map[common.Hash]*cachedNode{{}: { 314 children: make(map[common.Hash]uint16), 315 }}, 316 rawDirties: make(KvMap), 317 preimages: preimage, 318 } 319 return db 320 } 321 322 // DiskDB retrieves the persistent storage backing the trie database. 323 func (db *Database) DiskDB() ethdb.KeyValueStore { 324 return db.diskdb 325 } 326 327 // insert inserts a collapsed trie node into the memory database. 328 // The blob size must be specified to allow proper size tracking. 329 // All nodes inserted by this function will be reference tracked 330 // and in theory should only used for **trie nodes** insertion. 331 func (db *Database) insert(hash common.Hash, size int, node node) { 332 // If the node's already cached, skip 333 if _, ok := db.dirties[hash]; ok { 334 return 335 } 336 memcacheDirtyWriteMeter.Mark(int64(size)) 337 338 // Create the cached entry for this node 339 entry := &cachedNode{ 340 node: simplifyNode(node), 341 size: uint16(size), 342 flushPrev: db.newest, 343 } 344 entry.forChilds(func(child common.Hash) { 345 if c := db.dirties[child]; c != nil { 346 c.parents++ 347 } 348 }) 349 db.dirties[hash] = entry 350 351 // Update the flush-list endpoints 352 if db.oldest == (common.Hash{}) { 353 db.oldest, db.newest = hash, hash 354 } else { 355 db.dirties[db.newest].flushNext, db.newest = hash, hash 356 } 357 db.dirtiesSize += common.StorageSize(common.HashLength + entry.size) 358 } 359 360 // node retrieves a cached trie node from memory, or returns nil if none can be 361 // found in the memory cache. 362 func (db *Database) node(hash common.Hash) node { 363 // Retrieve the node from the clean cache if available 364 if db.cleans != nil { 365 if enc := db.cleans.Get(nil, hash[:]); enc != nil { 366 memcacheCleanHitMeter.Mark(1) 367 memcacheCleanReadMeter.Mark(int64(len(enc))) 368 return mustDecodeNode(hash[:], enc) 369 } 370 } 371 // Retrieve the node from the dirty cache if available 372 db.lock.RLock() 373 dirty := db.dirties[hash] 374 db.lock.RUnlock() 375 376 if dirty != nil { 377 memcacheDirtyHitMeter.Mark(1) 378 memcacheDirtyReadMeter.Mark(int64(dirty.size)) 379 return dirty.obj(hash) 380 } 381 memcacheDirtyMissMeter.Mark(1) 382 383 // Content unavailable in memory, attempt to retrieve from disk 384 enc, err := db.diskdb.Get(hash[:]) 385 if err != nil || enc == nil { 386 return nil 387 } 388 if db.cleans != nil { 389 db.cleans.Set(hash[:], enc) 390 memcacheCleanMissMeter.Mark(1) 391 memcacheCleanWriteMeter.Mark(int64(len(enc))) 392 } 393 return mustDecodeNode(hash[:], enc) 394 } 395 396 // Node retrieves an encoded cached trie node from memory. If it cannot be found 397 // cached, the method queries the persistent database for the content. 398 func (db *Database) Node(hash common.Hash) ([]byte, error) { 399 // It doesn't make sense to retrieve the metaroot 400 if hash == (common.Hash{}) { 401 return nil, errors.New("not found") 402 } 403 // Retrieve the node from the clean cache if available 404 if db.cleans != nil { 405 if enc := db.cleans.Get(nil, hash[:]); enc != nil { 406 memcacheCleanHitMeter.Mark(1) 407 memcacheCleanReadMeter.Mark(int64(len(enc))) 408 return enc, nil 409 } 410 } 411 // Retrieve the node from the dirty cache if available 412 db.lock.RLock() 413 dirty := db.dirties[hash] 414 db.lock.RUnlock() 415 416 if dirty != nil { 417 memcacheDirtyHitMeter.Mark(1) 418 memcacheDirtyReadMeter.Mark(int64(dirty.size)) 419 return dirty.rlp(), nil 420 } 421 memcacheDirtyMissMeter.Mark(1) 422 423 // Content unavailable in memory, attempt to retrieve from disk 424 enc := rawdb.ReadTrieNode(db.diskdb, hash) 425 if len(enc) != 0 { 426 if db.cleans != nil { 427 db.cleans.Set(hash[:], enc) 428 memcacheCleanMissMeter.Mark(1) 429 memcacheCleanWriteMeter.Mark(int64(len(enc))) 430 } 431 return enc, nil 432 } 433 return nil, errors.New("not found") 434 } 435 436 // Nodes retrieves the hashes of all the nodes cached within the memory database. 437 // This method is extremely expensive and should only be used to validate internal 438 // states in test code. 439 func (db *Database) Nodes() []common.Hash { 440 db.lock.RLock() 441 defer db.lock.RUnlock() 442 443 var hashes = make([]common.Hash, 0, len(db.dirties)) 444 for hash := range db.dirties { 445 if hash != (common.Hash{}) { // Special case for "root" references/nodes 446 hashes = append(hashes, hash) 447 } 448 } 449 return hashes 450 } 451 452 // Reference adds a new reference from a parent node to a child node. 453 // This function is used to add reference between internal trie node 454 // and external node(e.g. storage trie root), all internal trie nodes 455 // are referenced together by database itself. 456 func (db *Database) Reference(child common.Hash, parent common.Hash) { 457 db.lock.Lock() 458 defer db.lock.Unlock() 459 460 db.reference(child, parent) 461 } 462 463 // reference is the private locked version of Reference. 464 func (db *Database) reference(child common.Hash, parent common.Hash) { 465 // If the node does not exist, it's a node pulled from disk, skip 466 node, ok := db.dirties[child] 467 if !ok { 468 return 469 } 470 // If the reference already exists, only duplicate for roots 471 if db.dirties[parent].children == nil { 472 db.dirties[parent].children = make(map[common.Hash]uint16) 473 db.childrenSize += cachedNodeChildrenSize 474 } else if _, ok = db.dirties[parent].children[child]; ok && parent != (common.Hash{}) { 475 return 476 } 477 node.parents++ 478 db.dirties[parent].children[child]++ 479 if db.dirties[parent].children[child] == 1 { 480 db.childrenSize += common.HashLength + 2 // uint16 counter 481 } 482 } 483 484 // Dereference removes an existing reference from a root node. 485 func (db *Database) Dereference(root common.Hash) { 486 // Sanity check to ensure that the meta-root is not removed 487 if root == (common.Hash{}) { 488 log.Error("Attempted to dereference the trie cache meta root") 489 return 490 } 491 db.lock.Lock() 492 defer db.lock.Unlock() 493 494 nodes, storage, start := len(db.dirties), db.dirtiesSize, time.Now() 495 db.dereference(root, common.Hash{}) 496 497 db.gcnodes += uint64(nodes - len(db.dirties)) 498 db.gcsize += storage - db.dirtiesSize 499 db.gctime += time.Since(start) 500 501 memcacheGCTimeTimer.Update(time.Since(start)) 502 memcacheGCSizeMeter.Mark(int64(storage - db.dirtiesSize)) 503 memcacheGCNodesMeter.Mark(int64(nodes - len(db.dirties))) 504 505 log.Debug("Dereferenced trie from memory database", "nodes", nodes-len(db.dirties), "size", storage-db.dirtiesSize, "time", time.Since(start), 506 "gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.dirties), "livesize", db.dirtiesSize) 507 } 508 509 // dereference is the private locked version of Dereference. 510 func (db *Database) dereference(child common.Hash, parent common.Hash) { 511 // Dereference the parent-child 512 node := db.dirties[parent] 513 514 if node.children != nil && node.children[child] > 0 { 515 node.children[child]-- 516 if node.children[child] == 0 { 517 delete(node.children, child) 518 db.childrenSize -= (common.HashLength + 2) // uint16 counter 519 } 520 } 521 // If the child does not exist, it's a previously committed node. 522 node, ok := db.dirties[child] 523 if !ok { 524 return 525 } 526 // If there are no more references to the child, delete it and cascade 527 if node.parents > 0 { 528 // This is a special cornercase where a node loaded from disk (i.e. not in the 529 // memcache any more) gets reinjected as a new node (short node split into full, 530 // then reverted into short), causing a cached node to have no parents. That is 531 // no problem in itself, but don't make maxint parents out of it. 532 node.parents-- 533 } 534 if node.parents == 0 { 535 // Remove the node from the flush-list 536 switch child { 537 case db.oldest: 538 db.oldest = node.flushNext 539 db.dirties[node.flushNext].flushPrev = common.Hash{} 540 case db.newest: 541 db.newest = node.flushPrev 542 db.dirties[node.flushPrev].flushNext = common.Hash{} 543 default: 544 db.dirties[node.flushPrev].flushNext = node.flushNext 545 db.dirties[node.flushNext].flushPrev = node.flushPrev 546 } 547 // Dereference all children and delete the node 548 node.forChilds(func(hash common.Hash) { 549 db.dereference(hash, child) 550 }) 551 delete(db.dirties, child) 552 db.dirtiesSize -= common.StorageSize(common.HashLength + int(node.size)) 553 if node.children != nil { 554 db.childrenSize -= cachedNodeChildrenSize 555 } 556 } 557 } 558 559 // Cap iteratively flushes old but still referenced trie nodes until the total 560 // memory usage goes below the given threshold. 561 // 562 // Note, this method is a non-synchronized mutator. It is unsafe to call this 563 // concurrently with other mutators. 564 func (db *Database) Cap(limit common.StorageSize) error { 565 // Create a database batch to flush persistent data out. It is important that 566 // outside code doesn't see an inconsistent state (referenced data removed from 567 // memory cache during commit but not yet in persistent storage). This is ensured 568 // by only uncaching existing data when the database write finalizes. 569 nodes, storage, start := len(db.dirties), db.dirtiesSize, time.Now() 570 batch := db.diskdb.NewBatch() 571 572 // db.dirtiesSize only contains the useful data in the cache, but when reporting 573 // the total memory consumption, the maintenance metadata is also needed to be 574 // counted. 575 size := db.dirtiesSize + common.StorageSize((len(db.dirties)-1)*cachedNodeSize) 576 size += db.childrenSize - common.StorageSize(len(db.dirties[common.Hash{}].children)*(common.HashLength+2)) 577 578 // If the preimage cache got large enough, push to disk. If it's still small 579 // leave for later to deduplicate writes. 580 if db.preimages != nil { 581 db.preimages.commit(false) 582 } 583 // Keep committing nodes from the flush-list until we're below allowance 584 oldest := db.oldest 585 for size > limit && oldest != (common.Hash{}) { 586 // Fetch the oldest referenced node and push into the batch 587 node := db.dirties[oldest] 588 rawdb.WriteTrieNode(batch, oldest, node.rlp()) 589 590 // If we exceeded the ideal batch size, commit and reset 591 if batch.ValueSize() >= ethdb.IdealBatchSize { 592 if err := batch.Write(); err != nil { 593 log.Error("Failed to write flush list to disk", "err", err) 594 return err 595 } 596 batch.Reset() 597 } 598 // Iterate to the next flush item, or abort if the size cap was achieved. Size 599 // is the total size, including the useful cached data (hash -> blob), the 600 // cache item metadata, as well as external children mappings. 601 size -= common.StorageSize(common.HashLength + int(node.size) + cachedNodeSize) 602 if node.children != nil { 603 size -= common.StorageSize(cachedNodeChildrenSize + len(node.children)*(common.HashLength+2)) 604 } 605 oldest = node.flushNext 606 } 607 // Flush out any remainder data from the last batch 608 if err := batch.Write(); err != nil { 609 log.Error("Failed to write flush list to disk", "err", err) 610 return err 611 } 612 // Write successful, clear out the flushed data 613 db.lock.Lock() 614 defer db.lock.Unlock() 615 616 for db.oldest != oldest { 617 node := db.dirties[db.oldest] 618 delete(db.dirties, db.oldest) 619 db.oldest = node.flushNext 620 621 db.dirtiesSize -= common.StorageSize(common.HashLength + int(node.size)) 622 if node.children != nil { 623 db.childrenSize -= common.StorageSize(cachedNodeChildrenSize + len(node.children)*(common.HashLength+2)) 624 } 625 } 626 if db.oldest != (common.Hash{}) { 627 db.dirties[db.oldest].flushPrev = common.Hash{} 628 } 629 db.flushnodes += uint64(nodes - len(db.dirties)) 630 db.flushsize += storage - db.dirtiesSize 631 db.flushtime += time.Since(start) 632 633 memcacheFlushTimeTimer.Update(time.Since(start)) 634 memcacheFlushSizeMeter.Mark(int64(storage - db.dirtiesSize)) 635 memcacheFlushNodesMeter.Mark(int64(nodes - len(db.dirties))) 636 637 log.Debug("Persisted nodes from memory database", "nodes", nodes-len(db.dirties), "size", storage-db.dirtiesSize, "time", time.Since(start), 638 "flushnodes", db.flushnodes, "flushsize", db.flushsize, "flushtime", db.flushtime, "livenodes", len(db.dirties), "livesize", db.dirtiesSize) 639 640 return nil 641 } 642 643 // Commit iterates over all the children of a particular node, writes them out 644 // to disk, forcefully tearing down all references in both directions. As a side 645 // effect, all pre-images accumulated up to this point are also written. 646 // 647 // Note, this method is a non-synchronized mutator. It is unsafe to call this 648 // concurrently with other mutators. 649 func (db *Database) Commit(node common.Hash, report bool, callback func(common.Hash)) error { 650 // Create a database batch to flush persistent data out. It is important that 651 // outside code doesn't see an inconsistent state (referenced data removed from 652 // memory cache during commit but not yet in persistent storage). This is ensured 653 // by only uncaching existing data when the database write finalizes. 654 start := time.Now() 655 batch := db.diskdb.NewBatch() 656 657 db.lock.Lock() 658 for _, v := range db.rawDirties { 659 batch.Put(v.K, v.V) 660 } 661 for k := range db.rawDirties { 662 delete(db.rawDirties, k) 663 } 664 db.lock.Unlock() 665 if err := batch.Write(); err != nil { 666 return err 667 } 668 batch.Reset() 669 670 if (node == common.Hash{}) { 671 return nil 672 } 673 674 // Move all of the accumulated preimages into a write batch 675 if db.preimages != nil { 676 db.preimages.commit(true) 677 } 678 // Move the trie itself into the batch, flushing if enough data is accumulated 679 nodes, storage := len(db.dirties), db.dirtiesSize 680 681 uncacher := &cleaner{db} 682 if err := db.commit(node, batch, uncacher, callback); err != nil { 683 log.Error("Failed to commit trie from trie database", "err", err) 684 return err 685 } 686 // Trie mostly committed to disk, flush any batch leftovers 687 if err := batch.Write(); err != nil { 688 log.Error("Failed to write trie to disk", "err", err) 689 return err 690 } 691 // Uncache any leftovers in the last batch 692 db.lock.Lock() 693 defer db.lock.Unlock() 694 695 batch.Replay(uncacher) 696 batch.Reset() 697 698 // Reset the storage counters and bumped metrics 699 memcacheCommitTimeTimer.Update(time.Since(start)) 700 memcacheCommitSizeMeter.Mark(int64(storage - db.dirtiesSize)) 701 memcacheCommitNodesMeter.Mark(int64(nodes - len(db.dirties))) 702 703 logger := log.Info 704 if !report { 705 logger = log.Debug 706 } 707 logger("Persisted trie from memory database", "nodes", nodes-len(db.dirties)+int(db.flushnodes), "size", storage-db.dirtiesSize+db.flushsize, "time", time.Since(start)+db.flushtime, 708 "gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.dirties), "livesize", db.dirtiesSize) 709 710 // Reset the garbage collection statistics 711 db.gcnodes, db.gcsize, db.gctime = 0, 0, 0 712 db.flushnodes, db.flushsize, db.flushtime = 0, 0, 0 713 714 return nil 715 } 716 717 // commit is the private locked version of Commit. 718 func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleaner, callback func(common.Hash)) error { 719 // If the node does not exist, it's a previously committed node 720 node, ok := db.dirties[hash] 721 if !ok { 722 return nil 723 } 724 var err error 725 node.forChilds(func(child common.Hash) { 726 if err == nil { 727 err = db.commit(child, batch, uncacher, callback) 728 } 729 }) 730 if err != nil { 731 return err 732 } 733 // If we've reached an optimal batch size, commit and start over 734 rawdb.WriteTrieNode(batch, hash, node.rlp()) 735 if callback != nil { 736 callback(hash) 737 } 738 if batch.ValueSize() >= ethdb.IdealBatchSize { 739 if err := batch.Write(); err != nil { 740 return err 741 } 742 db.lock.Lock() 743 batch.Replay(uncacher) 744 batch.Reset() 745 db.lock.Unlock() 746 } 747 return nil 748 } 749 750 // cleaner is a database batch replayer that takes a batch of write operations 751 // and cleans up the trie database from anything written to disk. 752 type cleaner struct { 753 db *Database 754 } 755 756 // Put reacts to database writes and implements dirty data uncaching. This is the 757 // post-processing step of a commit operation where the already persisted trie is 758 // removed from the dirty cache and moved into the clean cache. The reason behind 759 // the two-phase commit is to ensure data availability while moving from memory 760 // to disk. 761 func (c *cleaner) Put(key []byte, rlp []byte) error { 762 hash := common.BytesToHash(key) 763 764 // If the node does not exist, we're done on this path 765 node, ok := c.db.dirties[hash] 766 if !ok { 767 return nil 768 } 769 // Node still exists, remove it from the flush-list 770 switch hash { 771 case c.db.oldest: 772 c.db.oldest = node.flushNext 773 c.db.dirties[node.flushNext].flushPrev = common.Hash{} 774 case c.db.newest: 775 c.db.newest = node.flushPrev 776 c.db.dirties[node.flushPrev].flushNext = common.Hash{} 777 default: 778 c.db.dirties[node.flushPrev].flushNext = node.flushNext 779 c.db.dirties[node.flushNext].flushPrev = node.flushPrev 780 } 781 // Remove the node from the dirty cache 782 delete(c.db.dirties, hash) 783 c.db.dirtiesSize -= common.StorageSize(common.HashLength + int(node.size)) 784 if node.children != nil { 785 c.db.dirtiesSize -= common.StorageSize(cachedNodeChildrenSize + len(node.children)*(common.HashLength+2)) 786 } 787 // Move the flushed node into the clean cache to prevent insta-reloads 788 if c.db.cleans != nil { 789 c.db.cleans.Set(hash[:], rlp) 790 memcacheCleanWriteMeter.Mark(int64(len(rlp))) 791 } 792 return nil 793 } 794 795 func (c *cleaner) Delete(key []byte) error { 796 panic("not implemented") 797 } 798 799 // Size returns the current storage size of the memory cache in front of the 800 // persistent database layer. 801 func (db *Database) Size() (common.StorageSize, common.StorageSize) { 802 db.lock.RLock() 803 defer db.lock.RUnlock() 804 805 // db.dirtiesSize only contains the useful data in the cache, but when reporting 806 // the total memory consumption, the maintenance metadata is also needed to be 807 // counted. 808 var metadataSize = common.StorageSize((len(db.dirties) - 1) * cachedNodeSize) 809 var metarootRefs = common.StorageSize(len(db.dirties[common.Hash{}].children) * (common.HashLength + 2)) 810 var preimageSize common.StorageSize 811 if db.preimages != nil { 812 preimageSize = db.preimages.size() 813 } 814 return db.dirtiesSize + db.childrenSize + metadataSize - metarootRefs, preimageSize 815 } 816 817 // saveCache saves clean state cache to given directory path 818 // using specified CPU cores. 819 func (db *Database) saveCache(dir string, threads int) error { 820 if db.cleans == nil { 821 return nil 822 } 823 log.Info("Writing clean trie cache to disk", "path", dir, "threads", threads) 824 825 start := time.Now() 826 err := db.cleans.SaveToFileConcurrent(dir, threads) 827 if err != nil { 828 log.Error("Failed to persist clean trie cache", "error", err) 829 return err 830 } 831 log.Info("Persisted the clean trie cache", "path", dir, "elapsed", common.PrettyDuration(time.Since(start))) 832 return nil 833 } 834 835 // SaveCache atomically saves fast cache data to the given dir using all 836 // available CPU cores. 837 func (db *Database) SaveCache(dir string) error { 838 return db.saveCache(dir, runtime.GOMAXPROCS(0)) 839 } 840 841 // SaveCachePeriodically atomically saves fast cache data to the given dir with 842 // the specified interval. All dump operation will only use a single CPU core. 843 func (db *Database) SaveCachePeriodically(dir string, interval time.Duration, stopCh <-chan struct{}) { 844 ticker := time.NewTicker(interval) 845 defer ticker.Stop() 846 847 for { 848 select { 849 case <-ticker.C: 850 db.saveCache(dir, 1) 851 case <-stopCh: 852 return 853 } 854 } 855 } 856 857 // EmptyRoot indicate what root is for an empty trie, it depends on its underlying implement (zktrie or common trie) 858 func (db *Database) EmptyRoot() common.Hash { 859 860 if db.Zktrie { 861 return common.Hash{} 862 } else { 863 return emptyRoot 864 } 865 }