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