journal.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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. package snapshot
  17. import (
  18. "bufio"
  19. "encoding/binary"
  20. "errors"
  21. "fmt"
  22. "io"
  23. "os"
  24. "time"
  25. "github.com/VictoriaMetrics/fastcache"
  26. "github.com/ethereum/go-ethereum/common"
  27. "github.com/ethereum/go-ethereum/core/rawdb"
  28. "github.com/ethereum/go-ethereum/ethdb"
  29. "github.com/ethereum/go-ethereum/log"
  30. "github.com/ethereum/go-ethereum/rlp"
  31. "github.com/ethereum/go-ethereum/trie"
  32. )
  33. // journalGenerator is a disk layer entry containing the generator progress marker.
  34. type journalGenerator struct {
  35. Wiping bool // Whether the database was in progress of being wiped
  36. Done bool // Whether the generator finished creating the snapshot
  37. Marker []byte
  38. Accounts uint64
  39. Slots uint64
  40. Storage uint64
  41. }
  42. // journalAccount is an account entry in a diffLayer's disk journal.
  43. type journalAccount struct {
  44. Hash common.Hash
  45. Blob []byte
  46. }
  47. // journalStorage is an account's storage map in a diffLayer's disk journal.
  48. type journalStorage struct {
  49. Hash common.Hash
  50. Keys []common.Hash
  51. Vals [][]byte
  52. }
  53. // loadSnapshot loads a pre-existing state snapshot backed by a key-value store.
  54. func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, journal string, cache int, root common.Hash) (snapshot, error) {
  55. // Retrieve the block number and hash of the snapshot, failing if no snapshot
  56. // is present in the database (or crashed mid-update).
  57. baseRoot := rawdb.ReadSnapshotRoot(diskdb)
  58. if baseRoot == (common.Hash{}) {
  59. return nil, errors.New("missing or corrupted snapshot")
  60. }
  61. base := &diskLayer{
  62. diskdb: diskdb,
  63. triedb: triedb,
  64. cache: fastcache.New(cache * 1024 * 1024),
  65. root: baseRoot,
  66. }
  67. // Open the journal, it must exist since even for 0 layer it stores whether
  68. // we've already generated the snapshot or are in progress only
  69. file, err := os.Open(journal)
  70. if err != nil {
  71. return nil, err
  72. }
  73. r := rlp.NewStream(file, 0)
  74. // Read the snapshot generation progress for the disk layer
  75. var generator journalGenerator
  76. if err := r.Decode(&generator); err != nil {
  77. return nil, fmt.Errorf("failed to load snapshot progress marker: %v", err)
  78. }
  79. // Load all the snapshot diffs from the journal
  80. snapshot, err := loadDiffLayer(base, r)
  81. if err != nil {
  82. return nil, err
  83. }
  84. // Entire snapshot journal loaded, sanity check the head and return
  85. // Journal doesn't exist, don't worry if it's not supposed to
  86. if head := snapshot.Root(); head != root {
  87. return nil, fmt.Errorf("head doesn't match snapshot: have %#x, want %#x", head, root)
  88. }
  89. // Everything loaded correctly, resume any suspended operations
  90. if !generator.Done {
  91. // If the generator was still wiping, restart one from scratch (fine for
  92. // now as it's rare and the wiper deletes the stuff it touches anyway, so
  93. // restarting won't incur a lot of extra database hops.
  94. var wiper chan struct{}
  95. if generator.Wiping {
  96. log.Info("Resuming previous snapshot wipe")
  97. wiper = wipeSnapshot(diskdb, false)
  98. }
  99. // Whether or not wiping was in progress, load any generator progress too
  100. base.genMarker = generator.Marker
  101. if base.genMarker == nil {
  102. base.genMarker = []byte{}
  103. }
  104. base.genAbort = make(chan chan *generatorStats)
  105. var origin uint64
  106. if len(generator.Marker) >= 8 {
  107. origin = binary.BigEndian.Uint64(generator.Marker)
  108. }
  109. go base.generate(&generatorStats{
  110. wiping: wiper,
  111. origin: origin,
  112. start: time.Now(),
  113. accounts: generator.Accounts,
  114. slots: generator.Slots,
  115. storage: common.StorageSize(generator.Storage),
  116. })
  117. }
  118. return snapshot, nil
  119. }
  120. // loadDiffLayer reads the next sections of a snapshot journal, reconstructing a new
  121. // diff and verifying that it can be linked to the requested parent.
  122. func loadDiffLayer(parent snapshot, r *rlp.Stream) (snapshot, error) {
  123. // Read the next diff journal entry
  124. var root common.Hash
  125. if err := r.Decode(&root); err != nil {
  126. // The first read may fail with EOF, marking the end of the journal
  127. if err == io.EOF {
  128. return parent, nil
  129. }
  130. return nil, fmt.Errorf("load diff root: %v", err)
  131. }
  132. var accounts []journalAccount
  133. if err := r.Decode(&accounts); err != nil {
  134. return nil, fmt.Errorf("load diff accounts: %v", err)
  135. }
  136. accountData := make(map[common.Hash][]byte)
  137. for _, entry := range accounts {
  138. accountData[entry.Hash] = entry.Blob
  139. }
  140. var storage []journalStorage
  141. if err := r.Decode(&storage); err != nil {
  142. return nil, fmt.Errorf("load diff storage: %v", err)
  143. }
  144. storageData := make(map[common.Hash]map[common.Hash][]byte)
  145. for _, entry := range storage {
  146. slots := make(map[common.Hash][]byte)
  147. for i, key := range entry.Keys {
  148. slots[key] = entry.Vals[i]
  149. }
  150. storageData[entry.Hash] = slots
  151. }
  152. return loadDiffLayer(newDiffLayer(parent, root, accountData, storageData), r)
  153. }
  154. // Journal is the internal version of Journal that also returns the journal file
  155. // so subsequent layers know where to write to.
  156. func (dl *diskLayer) Journal(path string) (io.WriteCloser, common.Hash, error) {
  157. // If the snapshot is currenty being generated, abort it
  158. var stats *generatorStats
  159. if dl.genAbort != nil {
  160. abort := make(chan *generatorStats)
  161. dl.genAbort <- abort
  162. if stats = <-abort; stats != nil {
  163. stats.Log("Journalling in-progress snapshot", dl.genMarker)
  164. }
  165. }
  166. // Ensure the layer didn't get stale
  167. dl.lock.RLock()
  168. defer dl.lock.RUnlock()
  169. if dl.stale {
  170. return nil, common.Hash{}, ErrSnapshotStale
  171. }
  172. // We've reached the bottom, open the journal
  173. file, err := os.Create(path)
  174. if err != nil {
  175. return nil, common.Hash{}, err
  176. }
  177. // Write out the generator marker
  178. entry := journalGenerator{
  179. Done: dl.genMarker == nil,
  180. Marker: dl.genMarker,
  181. }
  182. if stats != nil {
  183. entry.Wiping = (stats.wiping != nil)
  184. entry.Accounts = stats.accounts
  185. entry.Slots = stats.slots
  186. entry.Storage = uint64(stats.storage)
  187. }
  188. if err := rlp.Encode(file, entry); err != nil {
  189. file.Close()
  190. return nil, common.Hash{}, err
  191. }
  192. return file, dl.root, nil
  193. }
  194. // Journal is the internal version of Journal that also returns the journal file
  195. // so subsequent layers know where to write to.
  196. func (dl *diffLayer) Journal(path string) (io.WriteCloser, common.Hash, error) {
  197. // Journal the parent first
  198. writer, base, err := dl.parent.Journal(path)
  199. if err != nil {
  200. return nil, common.Hash{}, err
  201. }
  202. // Ensure the layer didn't get stale
  203. dl.lock.RLock()
  204. defer dl.lock.RUnlock()
  205. if dl.stale {
  206. writer.Close()
  207. return nil, common.Hash{}, ErrSnapshotStale
  208. }
  209. // Everything below was journalled, persist this layer too
  210. buf := bufio.NewWriter(writer)
  211. if err := rlp.Encode(buf, dl.root); err != nil {
  212. buf.Flush()
  213. writer.Close()
  214. return nil, common.Hash{}, err
  215. }
  216. accounts := make([]journalAccount, 0, len(dl.accountData))
  217. for hash, blob := range dl.accountData {
  218. accounts = append(accounts, journalAccount{Hash: hash, Blob: blob})
  219. }
  220. if err := rlp.Encode(buf, accounts); err != nil {
  221. buf.Flush()
  222. writer.Close()
  223. return nil, common.Hash{}, err
  224. }
  225. storage := make([]journalStorage, 0, len(dl.storageData))
  226. for hash, slots := range dl.storageData {
  227. keys := make([]common.Hash, 0, len(slots))
  228. vals := make([][]byte, 0, len(slots))
  229. for key, val := range slots {
  230. keys = append(keys, key)
  231. vals = append(vals, val)
  232. }
  233. storage = append(storage, journalStorage{Hash: hash, Keys: keys, Vals: vals})
  234. }
  235. if err := rlp.Encode(buf, storage); err != nil {
  236. buf.Flush()
  237. writer.Close()
  238. return nil, common.Hash{}, err
  239. }
  240. buf.Flush()
  241. return writer, base, nil
  242. }