database.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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. package rawdb
  17. import (
  18. "bytes"
  19. "errors"
  20. "fmt"
  21. "os"
  22. "sync/atomic"
  23. "time"
  24. "github.com/ethereum/go-ethereum/common"
  25. "github.com/ethereum/go-ethereum/ethdb"
  26. "github.com/ethereum/go-ethereum/ethdb/leveldb"
  27. "github.com/ethereum/go-ethereum/ethdb/memorydb"
  28. "github.com/ethereum/go-ethereum/log"
  29. "github.com/olekukonko/tablewriter"
  30. )
  31. // freezerdb is a database wrapper that enabled freezer data retrievals.
  32. type freezerdb struct {
  33. ethdb.KeyValueStore
  34. ethdb.AncientStore
  35. diffStore ethdb.KeyValueStore
  36. }
  37. // Close implements io.Closer, closing both the fast key-value store as well as
  38. // the slow ancient tables.
  39. func (frdb *freezerdb) Close() error {
  40. var errs []error
  41. if err := frdb.AncientStore.Close(); err != nil {
  42. errs = append(errs, err)
  43. }
  44. if err := frdb.KeyValueStore.Close(); err != nil {
  45. errs = append(errs, err)
  46. }
  47. if frdb.diffStore != nil {
  48. if err := frdb.diffStore.Close(); err != nil {
  49. errs = append(errs, err)
  50. }
  51. }
  52. if len(errs) != 0 {
  53. return fmt.Errorf("%v", errs)
  54. }
  55. return nil
  56. }
  57. func (frdb *freezerdb) DiffStore() ethdb.KeyValueStore {
  58. return frdb.diffStore
  59. }
  60. func (frdb *freezerdb) SetDiffStore(diff ethdb.KeyValueStore) {
  61. if frdb.diffStore != nil {
  62. frdb.diffStore.Close()
  63. }
  64. frdb.diffStore = diff
  65. }
  66. // Freeze is a helper method used for external testing to trigger and block until
  67. // a freeze cycle completes, without having to sleep for a minute to trigger the
  68. // automatic background run.
  69. func (frdb *freezerdb) Freeze(threshold uint64) error {
  70. if frdb.AncientStore.(*freezer).readonly {
  71. return errReadOnly
  72. }
  73. // Set the freezer threshold to a temporary value
  74. defer func(old uint64) {
  75. atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, old)
  76. }(atomic.LoadUint64(&frdb.AncientStore.(*freezer).threshold))
  77. atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, threshold)
  78. // Trigger a freeze cycle and block until it's done
  79. trigger := make(chan struct{}, 1)
  80. frdb.AncientStore.(*freezer).trigger <- trigger
  81. <-trigger
  82. return nil
  83. }
  84. // nofreezedb is a database wrapper that disables freezer data retrievals.
  85. type nofreezedb struct {
  86. ethdb.KeyValueStore
  87. diffStore ethdb.KeyValueStore
  88. }
  89. // HasAncient returns an error as we don't have a backing chain freezer.
  90. func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) {
  91. return false, errNotSupported
  92. }
  93. // Ancient returns an error as we don't have a backing chain freezer.
  94. func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
  95. return nil, errNotSupported
  96. }
  97. // Ancients returns an error as we don't have a backing chain freezer.
  98. func (db *nofreezedb) Ancients() (uint64, error) {
  99. return 0, errNotSupported
  100. }
  101. // AncientSize returns an error as we don't have a backing chain freezer.
  102. func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
  103. return 0, errNotSupported
  104. }
  105. // AppendAncient returns an error as we don't have a backing chain freezer.
  106. func (db *nofreezedb) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
  107. return errNotSupported
  108. }
  109. // TruncateAncients returns an error as we don't have a backing chain freezer.
  110. func (db *nofreezedb) TruncateAncients(items uint64) error {
  111. return errNotSupported
  112. }
  113. // Sync returns an error as we don't have a backing chain freezer.
  114. func (db *nofreezedb) Sync() error {
  115. return errNotSupported
  116. }
  117. func (db *nofreezedb) DiffStore() ethdb.KeyValueStore {
  118. return db.diffStore
  119. }
  120. func (db *nofreezedb) SetDiffStore(diff ethdb.KeyValueStore) {
  121. db.diffStore = diff
  122. }
  123. // NewDatabase creates a high level database on top of a given key-value data
  124. // store without a freezer moving immutable chain segments into cold storage.
  125. func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
  126. return &nofreezedb{
  127. KeyValueStore: db,
  128. }
  129. }
  130. // NewDatabaseWithFreezer creates a high level database on top of a given key-
  131. // value data store with a freezer moving immutable chain segments into cold
  132. // storage.
  133. func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace string, readonly bool) (ethdb.Database, error) {
  134. // Create the idle freezer instance
  135. frdb, err := newFreezer(freezer, namespace, readonly)
  136. if err != nil {
  137. return nil, err
  138. }
  139. // Since the freezer can be stored separately from the user's key-value database,
  140. // there's a fairly high probability that the user requests invalid combinations
  141. // of the freezer and database. Ensure that we don't shoot ourselves in the foot
  142. // by serving up conflicting data, leading to both datastores getting corrupted.
  143. //
  144. // - If both the freezer and key-value store is empty (no genesis), we just
  145. // initialized a new empty freezer, so everything's fine.
  146. // - If the key-value store is empty, but the freezer is not, we need to make
  147. // sure the user's genesis matches the freezer. That will be checked in the
  148. // blockchain, since we don't have the genesis block here (nor should we at
  149. // this point care, the key-value/freezer combo is valid).
  150. // - If neither the key-value store nor the freezer is empty, cross validate
  151. // the genesis hashes to make sure they are compatible. If they are, also
  152. // ensure that there's no gap between the freezer and sunsequently leveldb.
  153. // - If the key-value store is not empty, but the freezer is we might just be
  154. // upgrading to the freezer release, or we might have had a small chain and
  155. // not frozen anything yet. Ensure that no blocks are missing yet from the
  156. // key-value store, since that would mean we already had an old freezer.
  157. // If the genesis hash is empty, we have a new key-value store, so nothing to
  158. // validate in this method. If, however, the genesis hash is not nil, compare
  159. // it to the freezer content.
  160. if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 {
  161. if frozen, _ := frdb.Ancients(); frozen > 0 {
  162. // If the freezer already contains something, ensure that the genesis blocks
  163. // match, otherwise we might mix up freezers across chains and destroy both
  164. // the freezer and the key-value store.
  165. frgenesis, err := frdb.Ancient(freezerHashTable, 0)
  166. if err != nil {
  167. return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
  168. } else if !bytes.Equal(kvgenesis, frgenesis) {
  169. return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
  170. }
  171. // Key-value store and freezer belong to the same network. Ensure that they
  172. // are contiguous, otherwise we might end up with a non-functional freezer.
  173. if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 {
  174. // Subsequent header after the freezer limit is missing from the database.
  175. // Reject startup is the database has a more recent head.
  176. if *ReadHeaderNumber(db, ReadHeadHeaderHash(db)) > frozen-1 {
  177. return nil, fmt.Errorf("gap (#%d) in the chain between ancients and leveldb", frozen)
  178. }
  179. // Database contains only older data than the freezer, this happens if the
  180. // state was wiped and reinited from an existing freezer.
  181. }
  182. // Otherwise, key-value store continues where the freezer left off, all is fine.
  183. // We might have duplicate blocks (crash after freezer write but before key-value
  184. // store deletion, but that's fine).
  185. } else {
  186. // If the freezer is empty, ensure nothing was moved yet from the key-value
  187. // store, otherwise we'll end up missing data. We check block #1 to decide
  188. // if we froze anything previously or not, but do take care of databases with
  189. // only the genesis block.
  190. if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) {
  191. // Key-value store contains more data than the genesis block, make sure we
  192. // didn't freeze anything yet.
  193. if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 {
  194. return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
  195. }
  196. // Block #1 is still in the database, we're allowed to init a new feezer
  197. }
  198. // Otherwise, the head header is still the genesis, we're allowed to init a new
  199. // feezer.
  200. }
  201. }
  202. // Freezer is consistent with the key-value database, permit combining the two
  203. if !frdb.readonly {
  204. go frdb.freeze(db)
  205. }
  206. return &freezerdb{
  207. KeyValueStore: db,
  208. AncientStore: frdb,
  209. }, nil
  210. }
  211. // NewMemoryDatabase creates an ephemeral in-memory key-value database without a
  212. // freezer moving immutable chain segments into cold storage.
  213. func NewMemoryDatabase() ethdb.Database {
  214. return NewDatabase(memorydb.New())
  215. }
  216. // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database
  217. // with an initial starting capacity, but without a freezer moving immutable
  218. // chain segments into cold storage.
  219. func NewMemoryDatabaseWithCap(size int) ethdb.Database {
  220. return NewDatabase(memorydb.NewWithCap(size))
  221. }
  222. // NewLevelDBDatabase creates a persistent key-value database without a freezer
  223. // moving immutable chain segments into cold storage.
  224. func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
  225. db, err := leveldb.New(file, cache, handles, namespace, readonly)
  226. if err != nil {
  227. return nil, err
  228. }
  229. return NewDatabase(db), nil
  230. }
  231. // NewLevelDBDatabaseWithFreezer creates a persistent key-value database with a
  232. // freezer moving immutable chain segments into cold storage.
  233. func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer string, namespace string, readonly bool) (ethdb.Database, error) {
  234. kvdb, err := leveldb.New(file, cache, handles, namespace, readonly)
  235. if err != nil {
  236. return nil, err
  237. }
  238. frdb, err := NewDatabaseWithFreezer(kvdb, freezer, namespace, readonly)
  239. if err != nil {
  240. kvdb.Close()
  241. return nil, err
  242. }
  243. return frdb, nil
  244. }
  245. type counter uint64
  246. func (c counter) String() string {
  247. return fmt.Sprintf("%d", c)
  248. }
  249. func (c counter) Percentage(current uint64) string {
  250. return fmt.Sprintf("%d", current*100/uint64(c))
  251. }
  252. // stat stores sizes and count for a parameter
  253. type stat struct {
  254. size common.StorageSize
  255. count counter
  256. }
  257. // Add size to the stat and increase the counter by 1
  258. func (s *stat) Add(size common.StorageSize) {
  259. s.size += size
  260. s.count++
  261. }
  262. func (s *stat) Size() string {
  263. return s.size.String()
  264. }
  265. func (s *stat) Count() string {
  266. return s.count.String()
  267. }
  268. // InspectDatabase traverses the entire database and checks the size
  269. // of all different categories of data.
  270. func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
  271. it := db.NewIterator(keyPrefix, keyStart)
  272. defer it.Release()
  273. var (
  274. count int64
  275. start = time.Now()
  276. logged = time.Now()
  277. // Key-value store statistics
  278. headers stat
  279. bodies stat
  280. receipts stat
  281. tds stat
  282. numHashPairings stat
  283. hashNumPairings stat
  284. tries stat
  285. codes stat
  286. txLookups stat
  287. accountSnaps stat
  288. storageSnaps stat
  289. preimages stat
  290. bloomBits stat
  291. cliqueSnaps stat
  292. parliaSnaps stat
  293. // Ancient store statistics
  294. ancientHeadersSize common.StorageSize
  295. ancientBodiesSize common.StorageSize
  296. ancientReceiptsSize common.StorageSize
  297. ancientTdsSize common.StorageSize
  298. ancientHashesSize common.StorageSize
  299. // Les statistic
  300. chtTrieNodes stat
  301. bloomTrieNodes stat
  302. // Meta- and unaccounted data
  303. metadata stat
  304. unaccounted stat
  305. shutdownInfo stat
  306. // Totals
  307. total common.StorageSize
  308. )
  309. // Inspect key-value database first.
  310. for it.Next() {
  311. var (
  312. key = it.Key()
  313. size = common.StorageSize(len(key) + len(it.Value()))
  314. )
  315. total += size
  316. switch {
  317. case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
  318. headers.Add(size)
  319. case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
  320. bodies.Add(size)
  321. case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
  322. receipts.Add(size)
  323. case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
  324. tds.Add(size)
  325. case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
  326. numHashPairings.Add(size)
  327. case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
  328. hashNumPairings.Add(size)
  329. case len(key) == common.HashLength:
  330. tries.Add(size)
  331. case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
  332. codes.Add(size)
  333. case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
  334. txLookups.Add(size)
  335. case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
  336. accountSnaps.Add(size)
  337. case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
  338. storageSnaps.Add(size)
  339. case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength):
  340. preimages.Add(size)
  341. case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
  342. bloomBits.Add(size)
  343. case bytes.HasPrefix(key, BloomBitsIndexPrefix):
  344. bloomBits.Add(size)
  345. case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength:
  346. cliqueSnaps.Add(size)
  347. case bytes.HasPrefix(key, []byte("parlia-")) && len(key) == 7+common.HashLength:
  348. parliaSnaps.Add(size)
  349. case bytes.HasPrefix(key, []byte("cht-")) ||
  350. bytes.HasPrefix(key, []byte("chtIndexV2-")) ||
  351. bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie
  352. chtTrieNodes.Add(size)
  353. case bytes.HasPrefix(key, []byte("blt-")) ||
  354. bytes.HasPrefix(key, []byte("bltIndex-")) ||
  355. bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub
  356. bloomTrieNodes.Add(size)
  357. case bytes.Equal(key, uncleanShutdownKey):
  358. shutdownInfo.Add(size)
  359. default:
  360. var accounted bool
  361. for _, meta := range [][]byte{
  362. databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey,
  363. fastTrieProgressKey, snapshotDisabledKey, snapshotRootKey, snapshotJournalKey,
  364. snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
  365. uncleanShutdownKey, badBlockKey,
  366. } {
  367. if bytes.Equal(key, meta) {
  368. metadata.Add(size)
  369. accounted = true
  370. break
  371. }
  372. }
  373. if !accounted {
  374. unaccounted.Add(size)
  375. }
  376. }
  377. count++
  378. if count%1000 == 0 && time.Since(logged) > 8*time.Second {
  379. log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
  380. logged = time.Now()
  381. }
  382. }
  383. // Inspect append-only file store then.
  384. ancientSizes := []*common.StorageSize{&ancientHeadersSize, &ancientBodiesSize, &ancientReceiptsSize, &ancientHashesSize, &ancientTdsSize}
  385. for i, category := range []string{freezerHeaderTable, freezerBodiesTable, freezerReceiptTable, freezerHashTable, freezerDifficultyTable} {
  386. if size, err := db.AncientSize(category); err == nil {
  387. *ancientSizes[i] += common.StorageSize(size)
  388. total += common.StorageSize(size)
  389. }
  390. }
  391. // Get number of ancient rows inside the freezer
  392. ancients := counter(0)
  393. if count, err := db.Ancients(); err == nil {
  394. ancients = counter(count)
  395. }
  396. // Display the database statistic.
  397. stats := [][]string{
  398. {"Key-Value store", "Headers", headers.Size(), headers.Count()},
  399. {"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
  400. {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
  401. {"Key-Value store", "Difficulties", tds.Size(), tds.Count()},
  402. {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
  403. {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
  404. {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
  405. {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
  406. {"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
  407. {"Key-Value store", "Trie nodes", tries.Size(), tries.Count()},
  408. {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
  409. {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
  410. {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
  411. {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
  412. {"Key-Value store", "Parlia snapshots", parliaSnaps.Size(), parliaSnaps.Count()},
  413. {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
  414. {"Key-Value store", "Shutdown metadata", shutdownInfo.Size(), shutdownInfo.Count()},
  415. {"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()},
  416. {"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()},
  417. {"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()},
  418. {"Ancient store", "Difficulties", ancientTdsSize.String(), ancients.String()},
  419. {"Ancient store", "Block number->hash", ancientHashesSize.String(), ancients.String()},
  420. {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
  421. {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
  422. }
  423. table := tablewriter.NewWriter(os.Stdout)
  424. table.SetHeader([]string{"Database", "Category", "Size", "Items"})
  425. table.SetFooter([]string{"", "Total", total.String(), " "})
  426. table.AppendBulk(stats)
  427. table.Render()
  428. if unaccounted.size > 0 {
  429. log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
  430. }
  431. return nil
  432. }