Rework the data path and db type handling.

This commit modifies the way the data paths are handled.  Since there will
ultimately be more data associated with each network than just the block
database, the data path has been modified to be "namespaced" based on the
network.  This allows all data associated with a specific network to
simply use the data path without having to worry about conflicts with data
from other networks.

In addition, this commit renames the block database to "blocks" plus a
suffix which denotes the database type.  This prevents issues that would
otherwise arise if the user decides to use a different database type and
a file/folder with the same name already eixsts but is of the old database
type.  For most users this won't matter, but it does provide nice
properties for testing and development as well since it makes it easy to
go back and forth between database types.

This commit also includes code to upgrade the old database paths to the
new ones so the change is seamless for the user.

Finally, bump the version to 0.2.0.
This commit is contained in:
Dave Collins 2013-09-15 12:08:42 -05:00
parent 252ecf8b00
commit 629a1c9d06
5 changed files with 124 additions and 50 deletions

View file

@ -18,6 +18,11 @@ import (
const (
chanBufferSize = 50
// blockDbNamePrefix is the prefix for the block database name. The
// database type is appended to this value to form the full block
// database name.
blockDbNamePrefix = "blocks"
)
// blockMsg packages a bitcoin block message and the peer it came from together
@ -394,7 +399,13 @@ func newBlockManager(s *server) *blockManager {
// loadBlockDB opens the block database and returns a handle to it.
func loadBlockDB() (btcdb.Db, error) {
dbPath := filepath.Join(cfg.DataDir, activeNetParams.dbName)
// The database name is based on the database type.
dbName := blockDbNamePrefix + "_" + cfg.DbType
if cfg.DbType == "sqlite" {
dbName = dbName + ".db"
}
dbPath := filepath.Join(cfg.DataDir, dbName)
log.Infof("[BMGR] Loading block database from '%s'", dbPath)
db, err := btcdb.OpenDB(cfg.DbType, dbPath)
if err != nil {

View file

@ -25,11 +25,13 @@ const (
defaultMaxPeers = 8
defaultBanDuration = time.Hour * 24
defaultVerifyEnabled = false
defaultDbType = "sqlite"
)
var (
defaultConfigFile = filepath.Join(btcdHomeDir(), defaultConfigFilename)
defaultDataDir = filepath.Join(btcdHomeDir(), "db")
defaultDataDir = filepath.Join(btcdHomeDir(), "data")
knownDbTypes = []string{"leveldb", "sqlite"}
)
// config defines the configuration options for btcd.
@ -58,7 +60,7 @@ type config struct {
UseTor bool `long:"tor" description:"Specifies the proxy server used is a Tor node"`
TestNet3 bool `long:"testnet" description:"Use the test network"`
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
DbType string `long:"dbtype" description:"DB backend to use for Block Chain"`
DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"`
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
}
@ -99,6 +101,19 @@ func validLogLevel(logLevel string) bool {
return false
}
// validDbType returns whether or not dbType is a supported database type.
func validDbType(dbType string) bool {
// Would be interesting if btcdb had a 'SupportedDBs() []string'
// API to populate this field.
for _, knownType := range knownDbTypes {
if dbType == knownType {
return true
}
}
return false
}
// normalizePeerAddress returns addr with the default peer port appended if
// there is not already a port specified.
func normalizePeerAddress(addr string) string {
@ -180,6 +195,7 @@ func loadConfig() (*config, []string, error) {
BanDuration: defaultBanDuration,
ConfigFile: defaultConfigFile,
DataDir: defaultDataDir,
DbType: defaultDbType,
}
// Pre-parse the command line options to see if an alternative config
@ -249,13 +265,32 @@ func loadConfig() (*config, []string, error) {
// Validate debug log level.
if !validLogLevel(cfg.DebugLevel) {
str := "%s: The specified debug level is invalid -- parsed [%v]"
str := "%s: The specified debug level [%v] is invalid"
err := errors.New(fmt.Sprintf(str, "loadConfig", cfg.DebugLevel))
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
// Validate database type.
if !validDbType(cfg.DbType) {
str := "%s: The specified database type [%v] is invalid -- " +
"supported types %v"
err := errors.New(fmt.Sprintf(str, "loadConfig", cfg.DbType,
knownDbTypes))
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
// Append the network type to the data directory so it is "namespaced"
// per network. In addition to the block database, there are other
// pieces of data that are saved to disk such as address manager state.
// All data is specific to a network, so namespacing the data directory
// means each individual piece of serialized data does not have to
// worry about changing names per network and such.
cfg.DataDir = filepath.Join(cfg.DataDir, activeNetParams.netName)
// Don't allow ban durations that are too short.
if cfg.BanDuration < time.Duration(time.Second) {
str := "%s: The banduration option may not be less than 1s -- parsed [%v]"
@ -303,45 +338,7 @@ func loadConfig() (*config, []string, error) {
// Add default port to all added peer addresses if needed and remove
// duplicate addresses.
cfg.AddPeers = normalizeAndRemoveDuplicateAddresses(cfg.AddPeers)
cfg.ConnectPeers =
normalizeAndRemoveDuplicateAddresses(cfg.ConnectPeers)
cfg.ConnectPeers = normalizeAndRemoveDuplicateAddresses(cfg.ConnectPeers)
// determine which database backend to use
// would be interesting of btcdb had a 'supportedDBs() []string'
// API to populate this field.
knownDbs := []string{"leveldb", "sqlite"}
defaultDb := "sqlite"
if len(cfg.DbType) == 0 {
// if db was not specified, use heuristic to see what
// type the database is (if the specified database is a
// file then it is sqlite3, if it is a directory assume
// it is leveldb, if not found default to type _____
dbPath := filepath.Join(cfg.DataDir, activeNetParams.dbName)
fi, err := os.Stat(dbPath)
if err == nil {
if fi.IsDir() {
cfg.DbType = "leveldb"
} else {
cfg.DbType = "sqlite"
}
} else {
cfg.DbType = defaultDb
}
} else {
// does checking this here really make sense ?
typeVerified := false
for _, dbtype := range knownDbs {
if cfg.DbType == dbtype {
typeVerified = true
}
}
if typeVerified == false {
err := fmt.Errorf("Specified database type [%v] not in list of supported databases: %v", cfg.DbType, knownDbs)
fmt.Fprintln(os.Stderr, err)
return nil, nil, err
}
}
return &cfg, remainingArgs, nil
}

View file

@ -17,7 +17,7 @@ var activeNetParams = netParams(defaultBtcnet)
// params is used to group parameters for various networks such as the main
// network and test networks.
type params struct {
dbName string
netName string
btcnet btcwire.BitcoinNet
genesisBlock *btcwire.MsgBlock
genesisHash *btcwire.ShaHash
@ -32,7 +32,7 @@ type params struct {
// mainNetParams contains parameters specific to the main network
// (btcwire.MainNet).
var mainNetParams = params{
dbName: "btcd.db",
netName: "mainnet",
btcnet: btcwire.MainNet,
genesisBlock: btcchain.ChainParams(btcwire.MainNet).GenesisBlock,
genesisHash: btcchain.ChainParams(btcwire.MainNet).GenesisHash,
@ -52,7 +52,7 @@ var mainNetParams = params{
// regressionParams contains parameters specific to the regression test network
// (btcwire.TestNet).
var regressionParams = params{
dbName: "btcd_regtest.db",
netName: "regtest",
btcnet: btcwire.TestNet,
genesisBlock: btcchain.ChainParams(btcwire.TestNet).GenesisBlock,
genesisHash: btcchain.ChainParams(btcwire.TestNet).GenesisHash,
@ -67,7 +67,7 @@ var regressionParams = params{
// testNet3Params contains parameters specific to the test network (version 3)
// (btcwire.TestNet3).
var testNet3Params = params{
dbName: "btcd_testnet.db",
netName: "testnet",
btcnet: btcwire.TestNet3,
genesisBlock: btcchain.ChainParams(btcwire.TestNet3).GenesisBlock,
genesisHash: btcchain.ChainParams(btcwire.TestNet3).GenesisHash,

View file

@ -4,7 +4,73 @@
package main
// doUpgrades performs upgrades to btcd as new versions require it.
func doUpgrades() error {
import (
"os"
"path/filepath"
)
// upgradeDBPathNet moves the database for a specific network from its
// location prior to btcd version 0.2.0 and uses heuristics to ascertain the old
// database type to rename to the new format.
func upgradeDBPathNet(oldDbPath, netName string) error {
// Prior to version 0.2.0, the database was named the same thing for
// both sqlite and leveldb. Use heuristics to figure out the type
// of the database and move it to the new path and name introduced with
// version 0.2.0 accordingly.
fi, err := os.Stat(oldDbPath)
if err == nil {
oldDbType := "sqlite"
if fi.IsDir() {
oldDbType = "leveldb"
}
// The new database name is based on the database type and
// resides in the a directory named after the network type.
newDbRoot := filepath.Join(filepath.Dir(cfg.DataDir), netName)
newDbName := blockDbNamePrefix + "_" + oldDbType
if oldDbType == "sqlite" {
newDbName = newDbName + ".db"
}
newDbPath := filepath.Join(newDbRoot, newDbName)
// Create the new path if needed.
err = os.MkdirAll(newDbRoot, 0700)
if err != nil {
return err
}
// Move and rename the old database.
err := os.Rename(oldDbPath, newDbPath)
if err != nil {
return err
}
}
return nil
}
// upgradeDBPaths moves the databases from their locations prior to btcd
// version 0.2.0 to their new locations.
func upgradeDBPaths() error {
// Prior to version 0.2.0, the databases were in the "db" directory and
// their names were suffixed by "testnet" and "regtest" for their
// respective networks. Check for the old database and update it to the
// new path introduced with version 0.2.0 accodingly.
oldDbRoot := filepath.Join(btcdHomeDir(), "db")
upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd.db"), "mainnet")
upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_testnet.db"), "testnet")
upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_regtest.db"), "regtest")
// Remove the old db directory.
err := os.RemoveAll(oldDbRoot)
if err != nil {
return err
}
return nil
}
// doUpgrades performs upgrades to btcd as new versions require it.
func doUpgrades() error {
return upgradeDBPaths()
}

View file

@ -17,7 +17,7 @@ const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr
// versioning 2.0.0 spec (http://semver.org/).
const (
appMajor uint = 0
appMinor uint = 1
appMinor uint = 2
appPatch uint = 0
// appPreRelease MUST only contain characters from semanticAlphabet