github.com/ethereum/go-ethereum@v1.16.1/core/state/snapshot/disklayer.go (about) 1 // Copyright 2019 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 snapshot 18 19 import ( 20 "bytes" 21 "sync" 22 23 "github.com/VictoriaMetrics/fastcache" 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/rawdb" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/ethdb" 28 "github.com/ethereum/go-ethereum/rlp" 29 "github.com/ethereum/go-ethereum/triedb" 30 ) 31 32 // diskLayer is a low level persistent snapshot built on top of a key-value store. 33 type diskLayer struct { 34 diskdb ethdb.KeyValueStore // Key-value store containing the base snapshot 35 triedb *triedb.Database // Trie node cache for reconstruction purposes 36 cache *fastcache.Cache // Cache to avoid hitting the disk for direct access 37 38 root common.Hash // Root hash of the base snapshot 39 stale bool // Signals that the layer became stale (state progressed) 40 41 genMarker []byte // Marker for the state that's indexed during initial layer generation 42 genPending chan struct{} // Notification channel when generation is done (test synchronicity) 43 genAbort chan chan *generatorStats // Notification channel to abort generating the snapshot in this layer 44 45 lock sync.RWMutex 46 } 47 48 // Release releases underlying resources; specifically the fastcache requires 49 // Reset() in order to not leak memory. 50 // OBS: It does not invoke Close on the diskdb 51 func (dl *diskLayer) Release() error { 52 if dl.cache != nil { 53 dl.cache.Reset() 54 } 55 return nil 56 } 57 58 // Root returns root hash for which this snapshot was made. 59 func (dl *diskLayer) Root() common.Hash { 60 return dl.root 61 } 62 63 // Parent always returns nil as there's no layer below the disk. 64 func (dl *diskLayer) Parent() snapshot { 65 return nil 66 } 67 68 // Stale return whether this layer has become stale (was flattened across) or if 69 // it's still live. 70 func (dl *diskLayer) Stale() bool { 71 dl.lock.RLock() 72 defer dl.lock.RUnlock() 73 74 return dl.stale 75 } 76 77 // markStale sets the stale flag as true. 78 func (dl *diskLayer) markStale() { 79 dl.lock.Lock() 80 defer dl.lock.Unlock() 81 82 dl.stale = true 83 } 84 85 // Account directly retrieves the account associated with a particular hash in 86 // the snapshot slim data format. 87 func (dl *diskLayer) Account(hash common.Hash) (*types.SlimAccount, error) { 88 data, err := dl.AccountRLP(hash) 89 if err != nil { 90 return nil, err 91 } 92 if len(data) == 0 { // can be both nil and []byte{} 93 return nil, nil 94 } 95 account := new(types.SlimAccount) 96 if err := rlp.DecodeBytes(data, account); err != nil { 97 panic(err) 98 } 99 return account, nil 100 } 101 102 // AccountRLP directly retrieves the account RLP associated with a particular 103 // hash in the snapshot slim data format. 104 func (dl *diskLayer) AccountRLP(hash common.Hash) ([]byte, error) { 105 dl.lock.RLock() 106 defer dl.lock.RUnlock() 107 108 // If the layer was flattened into, consider it invalid (any live reference to 109 // the original should be marked as unusable). 110 if dl.stale { 111 return nil, ErrSnapshotStale 112 } 113 // If the layer is being generated, ensure the requested hash has already been 114 // covered by the generator. 115 if dl.genMarker != nil && bytes.Compare(hash[:], dl.genMarker) > 0 { 116 return nil, ErrNotCoveredYet 117 } 118 // If we're in the disk layer, all diff layers missed 119 snapshotDirtyAccountMissMeter.Mark(1) 120 121 // Try to retrieve the account from the memory cache 122 if blob, found := dl.cache.HasGet(nil, hash[:]); found { 123 snapshotCleanAccountHitMeter.Mark(1) 124 snapshotCleanAccountReadMeter.Mark(int64(len(blob))) 125 return blob, nil 126 } 127 // Cache doesn't contain account, pull from disk and cache for later 128 blob := rawdb.ReadAccountSnapshot(dl.diskdb, hash) 129 dl.cache.Set(hash[:], blob) 130 131 snapshotCleanAccountMissMeter.Mark(1) 132 if n := len(blob); n > 0 { 133 snapshotCleanAccountWriteMeter.Mark(int64(n)) 134 } else { 135 snapshotCleanAccountInexMeter.Mark(1) 136 } 137 return blob, nil 138 } 139 140 // Storage directly retrieves the storage data associated with a particular hash, 141 // within a particular account. 142 func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, error) { 143 dl.lock.RLock() 144 defer dl.lock.RUnlock() 145 146 // If the layer was flattened into, consider it invalid (any live reference to 147 // the original should be marked as unusable). 148 if dl.stale { 149 return nil, ErrSnapshotStale 150 } 151 key := append(accountHash[:], storageHash[:]...) 152 153 // If the layer is being generated, ensure the requested hash has already been 154 // covered by the generator. 155 if dl.genMarker != nil && bytes.Compare(key, dl.genMarker) > 0 { 156 return nil, ErrNotCoveredYet 157 } 158 // If we're in the disk layer, all diff layers missed 159 snapshotDirtyStorageMissMeter.Mark(1) 160 161 // Try to retrieve the storage slot from the memory cache 162 if blob, found := dl.cache.HasGet(nil, key); found { 163 snapshotCleanStorageHitMeter.Mark(1) 164 snapshotCleanStorageReadMeter.Mark(int64(len(blob))) 165 return blob, nil 166 } 167 // Cache doesn't contain storage slot, pull from disk and cache for later 168 blob := rawdb.ReadStorageSnapshot(dl.diskdb, accountHash, storageHash) 169 dl.cache.Set(key, blob) 170 171 snapshotCleanStorageMissMeter.Mark(1) 172 if n := len(blob); n > 0 { 173 snapshotCleanStorageWriteMeter.Mark(int64(n)) 174 } else { 175 snapshotCleanStorageInexMeter.Mark(1) 176 } 177 return blob, nil 178 } 179 180 // Update creates a new layer on top of the existing snapshot diff tree with 181 // the specified data items. Note, the maps are retained by the method to avoid 182 // copying everything. 183 func (dl *diskLayer) Update(blockHash common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer { 184 return newDiffLayer(dl, blockHash, accounts, storage) 185 } 186 187 // stopGeneration aborts the state snapshot generation if it is currently running. 188 func (dl *diskLayer) stopGeneration() { 189 dl.lock.RLock() 190 generating := dl.genMarker != nil 191 dl.lock.RUnlock() 192 if !generating { 193 return 194 } 195 if dl.genAbort != nil { 196 abort := make(chan *generatorStats) 197 dl.genAbort <- abort 198 <-abort 199 } 200 }