db_upgrade.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // Copyright 2016 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 eth implements the Ethereum protocol.
  17. package eth
  18. import (
  19. "bytes"
  20. "time"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/core"
  23. "github.com/ethereum/go-ethereum/ethdb"
  24. "github.com/ethereum/go-ethereum/log"
  25. "github.com/ethereum/go-ethereum/rlp"
  26. )
  27. var deduplicateData = []byte("dbUpgrade_20170714deduplicateData")
  28. // upgradeDeduplicateData checks the chain database version and
  29. // starts a background process to make upgrades if necessary.
  30. // Returns a stop function that blocks until the process has
  31. // been safely stopped.
  32. func upgradeDeduplicateData(db ethdb.Database) func() error {
  33. // If the database is already converted or empty, bail out
  34. data, _ := db.Get(deduplicateData)
  35. if len(data) > 0 && data[0] == 42 {
  36. return nil
  37. }
  38. if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 {
  39. db.Put(deduplicateData, []byte{42})
  40. return nil
  41. }
  42. // Start the deduplication upgrade on a new goroutine
  43. log.Warn("Upgrading database to use lookup entries")
  44. stop := make(chan chan error)
  45. go func() {
  46. // Create an iterator to read the entire database and covert old lookup entires
  47. it := db.(*ethdb.LDBDatabase).NewIterator()
  48. defer func() {
  49. if it != nil {
  50. it.Release()
  51. }
  52. }()
  53. var (
  54. converted uint64
  55. failed error
  56. )
  57. for failed == nil && it.Next() {
  58. // Skip any entries that don't look like old transaction meta entires (<hash>0x01)
  59. key := it.Key()
  60. if len(key) != common.HashLength+1 || key[common.HashLength] != 0x01 {
  61. continue
  62. }
  63. // Skip any entries that don't contain metadata (name clash between <hash>0x01 and <some-prefix><hash>)
  64. var meta struct {
  65. BlockHash common.Hash
  66. BlockIndex uint64
  67. Index uint64
  68. }
  69. if err := rlp.DecodeBytes(it.Value(), &meta); err != nil {
  70. continue
  71. }
  72. // Skip any already upgraded entries (clash due to <hash> ending with 0x01 (old suffix))
  73. hash := key[:common.HashLength]
  74. if hash[0] == byte('l') {
  75. // Potential clash, the "old" `hash` must point to a live transaction.
  76. if tx, _, _, _ := core.GetTransaction(db, common.BytesToHash(hash)); tx == nil || !bytes.Equal(tx.Hash().Bytes(), hash) {
  77. continue
  78. }
  79. }
  80. // Convert the old metadata to a new lookup entry, delete duplicate data
  81. if failed = db.Put(append([]byte("l"), hash...), it.Value()); failed == nil { // Write the new looku entry
  82. if failed = db.Delete(hash); failed == nil { // Delete the duplicate transaction data
  83. if failed = db.Delete(append([]byte("receipts-"), hash...)); failed == nil { // Delete the duplicate receipt data
  84. if failed = db.Delete(key); failed != nil { // Delete the old transaction metadata
  85. break
  86. }
  87. }
  88. }
  89. }
  90. // Bump the conversion counter, and recreate the iterator occasionally to
  91. // avoid too high memory consumption.
  92. converted++
  93. if converted%100000 == 0 {
  94. it.Release()
  95. it = db.(*ethdb.LDBDatabase).NewIterator()
  96. it.Seek(key)
  97. log.Info("Deduplicating database entries", "deduped", converted)
  98. }
  99. // Check for termination, or continue after a bit of a timeout
  100. select {
  101. case errc := <-stop:
  102. errc <- nil
  103. return
  104. case <-time.After(time.Microsecond * 100):
  105. }
  106. }
  107. // Upgrade finished, mark a such and terminate
  108. if failed == nil {
  109. log.Info("Database deduplication successful", "deduped", converted)
  110. db.Put(deduplicateData, []byte{42})
  111. } else {
  112. log.Error("Database deduplication failed", "deduped", converted, "err", failed)
  113. }
  114. it.Release()
  115. it = nil
  116. errc := <-stop
  117. errc <- failed
  118. }()
  119. // Assembly the cancellation callback
  120. return func() error {
  121. errc := make(chan error)
  122. stop <- errc
  123. return <-errc
  124. }
  125. }