github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/consensus/database.go (about)

     1  package consensus
     2  
     3  // database.go contains functions to initialize the database and report
     4  // inconsistencies. All of the database-specific logic belongs here.
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"os"
    10  
    11  	"github.com/NebulousLabs/Sia/build"
    12  	"github.com/NebulousLabs/Sia/encoding"
    13  	"github.com/NebulousLabs/Sia/persist"
    14  
    15  	"github.com/NebulousLabs/bolt"
    16  )
    17  
    18  var (
    19  	errRepeatInsert   = errors.New("attempting to add an already existing item to the consensus set")
    20  	errNilBucket      = errors.New("using a bucket that does not exist")
    21  	errNilItem        = errors.New("requested item does not exist")
    22  	errDBInconsistent = errors.New("database guard indicates inconsistency within database")
    23  	errNonEmptyBucket = errors.New("cannot remove a map with objects still in it")
    24  
    25  	dbMetadata = persist.Metadata{
    26  		Header:  "Consensus Set Database",
    27  		Version: "0.5.0",
    28  	}
    29  )
    30  
    31  type (
    32  	// dbBucket represents a collection of key/value pairs inside the database.
    33  	dbBucket interface {
    34  		Get(key []byte) []byte
    35  	}
    36  
    37  	// dbTx represents a read-only transaction on the database that can be used
    38  	// for retrieving values.
    39  	dbTx interface {
    40  		Bucket(name []byte) dbBucket
    41  	}
    42  
    43  	// boltTxWrapper wraps a bolt.Tx so that it matches the dbTx interface. The
    44  	// wrap is necessary because bolt.Tx.Bucket() returns a fixed type
    45  	// (bolt.Bucket), but we want it to return an interface (dbBucket).
    46  	boltTxWrapper struct {
    47  		tx *bolt.Tx
    48  	}
    49  )
    50  
    51  // Bucket returns the dbBucket associated with the given bucket name.
    52  func (b boltTxWrapper) Bucket(name []byte) dbBucket {
    53  	return b.tx.Bucket(name)
    54  }
    55  
    56  // replaceDatabase backs up the existing database and creates a new one.
    57  func (cs *ConsensusSet) replaceDatabase(filename string) error {
    58  	// Rename the existing database and create a new one.
    59  	fmt.Println("Outdated consensus database... backing up and replacing")
    60  	err := os.Rename(filename, filename+".bck")
    61  	if err != nil {
    62  		return errors.New("error while backing up consensus database: " + err.Error())
    63  	}
    64  
    65  	// Try again to create a new database, this time without checking for an
    66  	// outdated database error.
    67  	cs.db, err = persist.OpenDatabase(dbMetadata, filename)
    68  	if err != nil {
    69  		return errors.New("error opening consensus database: " + err.Error())
    70  	}
    71  	return nil
    72  }
    73  
    74  // openDB loads the set database and populates it with the necessary buckets
    75  func (cs *ConsensusSet) openDB(filename string) (err error) {
    76  	cs.db, err = persist.OpenDatabase(dbMetadata, filename)
    77  	if err == persist.ErrBadVersion {
    78  		return cs.replaceDatabase(filename)
    79  	}
    80  	if err != nil {
    81  		return errors.New("error opening consensus database: " + err.Error())
    82  	}
    83  	return nil
    84  }
    85  
    86  // dbInitialized returns true if the database appears to be initialized, false
    87  // if not. Checking for the existence of the siafund pool bucket is typically
    88  // sufficient to determine whether the database has gone through the
    89  // initialization process.
    90  func dbInitialized(tx *bolt.Tx) bool {
    91  	return tx.Bucket(SiafundPool) != nil
    92  }
    93  
    94  // initDB is run if there is no existing consensus database, creating a
    95  // database with all the required buckets and sane initial values.
    96  func (cs *ConsensusSet) initDB(tx *bolt.Tx) error {
    97  	// Create the compononents of the database.
    98  	err := cs.createConsensusDB(tx)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	err = cs.createChangeLog(tx)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	// Place a 'false' in the consistency bucket to indicate that no
   108  	// inconsistencies have been found.
   109  	err = tx.Bucket(Consistency).Put(Consistency, encoding.Marshal(false))
   110  	if err != nil {
   111  		return err
   112  	}
   113  	return nil
   114  }
   115  
   116  // inconsistencyDetected indicates whether inconsistency has been detected
   117  // within the database.
   118  func inconsistencyDetected(tx *bolt.Tx) (detected bool) {
   119  	inconsistencyBytes := tx.Bucket(Consistency).Get(Consistency)
   120  	err := encoding.Unmarshal(inconsistencyBytes, &detected)
   121  	if build.DEBUG && err != nil {
   122  		panic(err)
   123  	}
   124  	return detected
   125  }
   126  
   127  // markInconsistency flags the database to indicate that inconsistency has been
   128  // detected.
   129  func markInconsistency(tx *bolt.Tx) {
   130  	// Place a 'true' in the consistency bucket to indicate that
   131  	// inconsistencies have been found.
   132  	err := tx.Bucket(Consistency).Put(Consistency, encoding.Marshal(true))
   133  	if build.DEBUG && err != nil {
   134  		panic(err)
   135  	}
   136  
   137  }