github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/eth/db_upgrade.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 // Package eth implements the Sberex protocol. 13 package eth 14 15 import ( 16 "bytes" 17 "time" 18 19 "github.com/Sberex/go-sberex/common" 20 "github.com/Sberex/go-sberex/core" 21 "github.com/Sberex/go-sberex/ethdb" 22 "github.com/Sberex/go-sberex/log" 23 "github.com/Sberex/go-sberex/rlp" 24 ) 25 26 var deduplicateData = []byte("dbUpgrade_20170714deduplicateData") 27 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 46 go func() { 47 // Create an iterator to read the entire database and covert old lookup entires 48 it := db.(*ethdb.LDBDatabase).NewIterator() 49 defer func() { 50 if it != nil { 51 it.Release() 52 } 53 }() 54 55 var ( 56 converted uint64 57 failed error 58 ) 59 for failed == nil && it.Next() { 60 // Skip any entries that don't look like old transaction meta entires (<hash>0x01) 61 key := it.Key() 62 if len(key) != common.HashLength+1 || key[common.HashLength] != 0x01 { 63 continue 64 } 65 // Skip any entries that don't contain metadata (name clash between <hash>0x01 and <some-prefix><hash>) 66 var meta struct { 67 BlockHash common.Hash 68 BlockIndex uint64 69 Index uint64 70 } 71 if err := rlp.DecodeBytes(it.Value(), &meta); err != nil { 72 continue 73 } 74 // Skip any already upgraded entries (clash due to <hash> ending with 0x01 (old suffix)) 75 hash := key[:common.HashLength] 76 77 if hash[0] == byte('l') { 78 // Potential clash, the "old" `hash` must point to a live transaction. 79 if tx, _, _, _ := core.GetTransaction(db, common.BytesToHash(hash)); tx == nil || !bytes.Equal(tx.Hash().Bytes(), hash) { 80 continue 81 } 82 } 83 // Convert the old metadata to a new lookup entry, delete duplicate data 84 if failed = db.Put(append([]byte("l"), hash...), it.Value()); failed == nil { // Write the new looku entry 85 if failed = db.Delete(hash); failed == nil { // Delete the duplicate transaction data 86 if failed = db.Delete(append([]byte("receipts-"), hash...)); failed == nil { // Delete the duplicate receipt data 87 if failed = db.Delete(key); failed != nil { // Delete the old transaction metadata 88 break 89 } 90 } 91 } 92 } 93 // Bump the conversion counter, and recreate the iterator occasionally to 94 // avoid too high memory consumption. 95 converted++ 96 if converted%100000 == 0 { 97 it.Release() 98 it = db.(*ethdb.LDBDatabase).NewIterator() 99 it.Seek(key) 100 101 log.Info("Deduplicating database entries", "deduped", converted) 102 } 103 // Check for termination, or continue after a bit of a timeout 104 select { 105 case errc := <-stop: 106 errc <- nil 107 return 108 case <-time.After(time.Microsecond * 100): 109 } 110 } 111 // Upgrade finished, mark a such and terminate 112 if failed == nil { 113 log.Info("Database deduplication successful", "deduped", converted) 114 db.Put(deduplicateData, []byte{42}) 115 } else { 116 log.Error("Database deduplication failed", "deduped", converted, "err", failed) 117 } 118 it.Release() 119 it = nil 120 121 errc := <-stop 122 errc <- failed 123 }() 124 // Assembly the cancellation callback 125 return func() error { 126 errc := make(chan error) 127 stop <- errc 128 return <-errc 129 } 130 }