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  }