Merge pull request #4681 from filecoin-project/badger-viewable

move to native badger blockstore; leverage zero-copy View() to deserialize in-place
This commit is contained in:
Aayush Rajasekaran
2020-11-10 18:08:12 -05:00
committed by GitHub
27 changed files with 1579 additions and 403 deletions
+2 -2
View File
@@ -77,12 +77,12 @@ func MessagePool(lc fx.Lifecycle, sm *stmgr.StateManager, ps *pubsub.PubSub, ds
}
func ChainRawBlockstore(lc fx.Lifecycle, mctx helpers.MetricsCtx, r repo.LockedRepo) (dtypes.ChainRawBlockstore, error) {
blocks, err := r.Datastore("/chain")
bs, err := r.Blockstore(repo.BlockstoreChain)
if err != nil {
return nil, err
}
bs := blockstore.NewBlockstore(blocks)
// TODO potentially replace this cached blockstore by a CBOR cache.
cbs, err := blockstore.CachedBlockstore(helpers.LifecycleCtx(mctx, lc), bs, blockstore.DefaultCacheOpts())
if err != nil {
return nil, err
+51
View File
@@ -0,0 +1,51 @@
package repo
import badgerbs "github.com/filecoin-project/lotus/lib/blockstore/badger"
// BadgerBlockstoreOptions returns the badger options to apply for the provided
// domain.
func BadgerBlockstoreOptions(domain BlockstoreDomain, path string, readonly bool) (badgerbs.Options, error) {
if domain != BlockstoreChain {
return badgerbs.Options{}, ErrInvalidBlockstoreDomain
}
opts := badgerbs.DefaultOptions(path)
// Due to legacy usage of blockstore.Blockstore, over a datastore, all
// blocks are prefixed with this namespace. In the future, this can go away,
// in order to shorten keys, but it'll require a migration.
opts.Prefix = "/blocks/"
// Blockstore values are immutable; therefore we do not expect any
// conflicts to emerge.
opts.DetectConflicts = false
// This is to optimize the database on close so it can be opened
// read-only and efficiently queried. We don't do that and hanging on
// stop isn't nice.
opts.CompactL0OnClose = false
// The alternative is "crash on start and tell the user to fix it". This
// will truncate corrupt and unsynced data, which we don't guarantee to
// persist anyways.
opts.Truncate = true
// We mmap the index and the value logs; this is important to enable
// zero-copy value access.
opts.ValueLogLoadingMode = badgerbs.MemoryMap
opts.TableLoadingMode = badgerbs.MemoryMap
// Embed only values < 128 bytes in the LSM tree; larger values are stored
// in value logs.
opts.ValueThreshold = 128
// Default table size is already 64MiB. This is here to make it explicit.
opts.MaxTableSize = 64 << 20
// NOTE: The chain blockstore doesn't require any GC (blocks are never
// deleted). This will change if we move to a tiered blockstore.
opts.ReadOnly = readonly
return opts, nil
}
+40
View File
@@ -14,6 +14,7 @@ import (
"github.com/BurntSushi/toml"
"github.com/ipfs/go-datastore"
fslock "github.com/ipfs/go-fs-lock"
blockstore "github.com/ipfs/go-ipfs-blockstore"
logging "github.com/ipfs/go-log/v2"
"github.com/mitchellh/go-homedir"
"github.com/multiformats/go-base32"
@@ -22,6 +23,8 @@ import (
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
lblockstore "github.com/filecoin-project/lotus/lib/blockstore"
badgerbs "github.com/filecoin-project/lotus/lib/blockstore/badger"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/config"
@@ -257,6 +260,10 @@ type fsLockedRepo struct {
dsErr error
dsOnce sync.Once
bs blockstore.Blockstore
bsErr error
bsOnce sync.Once
storageLk sync.Mutex
configLk sync.Mutex
}
@@ -279,11 +286,44 @@ func (fsr *fsLockedRepo) Close() error {
}
}
// type assertion will return ok=false if fsr.bs is nil altogether.
if c, ok := fsr.bs.(io.Closer); ok && c != nil {
if err := c.Close(); err != nil {
return xerrors.Errorf("could not close blockstore: %w", err)
}
}
err = fsr.closer.Close()
fsr.closer = nil
return err
}
// Blockstore returns a blockstore for the provided data domain.
func (fsr *fsLockedRepo) Blockstore(domain BlockstoreDomain) (blockstore.Blockstore, error) {
if domain != BlockstoreChain {
return nil, ErrInvalidBlockstoreDomain
}
fsr.bsOnce.Do(func() {
path := fsr.join(filepath.Join(fsDatastore, "chain"))
readonly := fsr.readonly
opts, err := BadgerBlockstoreOptions(domain, path, readonly)
if err != nil {
fsr.bsErr = err
return
}
bs, err := badgerbs.Open(opts)
if err != nil {
fsr.bsErr = err
}
fsr.bs = lblockstore.WrapIDStore(bs)
})
return fsr.bs, fsr.bsErr
}
// join joins path elements with fsr.path
func (fsr *fsLockedRepo) join(paths ...string) string {
return filepath.Join(append([]string{fsr.path}, paths...)...)
-16
View File
@@ -16,17 +16,7 @@ import (
type dsCtor func(path string, readonly bool) (datastore.Batching, error)
func ChainBadgerOptions() badger.Options {
opts := badger.DefaultOptions
opts.GcInterval = 0 // disable GC for chain datastore
opts.Options = dgbadger.DefaultOptions("").WithTruncate(true).
WithValueThreshold(128)
return opts
}
var fsDatastores = map[string]dsCtor{
"chain": chainBadgerDs,
"metadata": levelDs,
// Those need to be fast for large writes... but also need a really good GC :c
@@ -35,12 +25,6 @@ var fsDatastores = map[string]dsCtor{
"client": badgerDs, // client specific
}
func chainBadgerDs(path string, readonly bool) (datastore.Batching, error) {
opts := ChainBadgerOptions()
opts.ReadOnly = readonly
return badger.NewDatastore(path, &opts)
}
func badgerDs(path string, readonly bool) (datastore.Batching, error) {
opts := badger.DefaultOptions
opts.ReadOnly = readonly
+19
View File
@@ -4,6 +4,7 @@ import (
"errors"
"github.com/ipfs/go-datastore"
blockstore "github.com/ipfs/go-ipfs-blockstore"
"github.com/multiformats/go-multiaddr"
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
@@ -12,11 +13,26 @@ import (
"github.com/filecoin-project/lotus/chain/types"
)
// BlockstoreDomain represents the domain of a blockstore.
type BlockstoreDomain string
const (
// BlockstoreChain represents the blockstore domain for chain data.
// Right now, this includes chain objects (tipsets, blocks, messages), as
// well as state. In the future, they may get segregated into different
// domains.
BlockstoreChain = BlockstoreDomain("chain")
)
var (
ErrNoAPIEndpoint = errors.New("API not running (no endpoint)")
ErrNoAPIToken = errors.New("API token not set")
ErrRepoAlreadyLocked = errors.New("repo is already locked (lotus daemon already running)")
ErrClosedRepo = errors.New("repo is no longer open")
// ErrInvalidBlockstoreDomain is returned by LockedRepo#Blockstore() when
// an unrecognized domain is requested.
ErrInvalidBlockstoreDomain = errors.New("invalid blockstore domain")
)
type Repo interface {
@@ -37,6 +53,9 @@ type LockedRepo interface {
// Returns datastore defined in this repo.
Datastore(namespace string) (datastore.Batching, error)
// Blockstore returns an IPLD blockstore for the requested domain.
Blockstore(domain BlockstoreDomain) (blockstore.Blockstore, error)
// Returns config in this repo
Config() (interface{}, error)
SetConfig(func(interface{})) error
+17 -9
View File
@@ -14,10 +14,10 @@ import (
"github.com/multiformats/go-multiaddr"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
"github.com/filecoin-project/lotus/lib/blockstore"
"github.com/filecoin-project/lotus/node/config"
)
@@ -31,8 +31,9 @@ type MemRepo struct {
repoLock chan struct{}
token *byte
datastore datastore.Datastore
keystore map[string]types.KeyInfo
datastore datastore.Datastore
keystore map[string]types.KeyInfo
blockstore blockstore.Blockstore
// given a repo type, produce the default config
configF func(t RepoType) interface{}
@@ -158,11 +159,11 @@ func NewMemory(opts *MemRepoOptions) *MemRepo {
}
return &MemRepo{
repoLock: make(chan struct{}, 1),
datastore: opts.Ds,
configF: opts.ConfigF,
keystore: opts.KeyStore,
repoLock: make(chan struct{}, 1),
blockstore: blockstore.WrapIDStore(blockstore.NewTemporarySync()),
datastore: opts.Ds,
configF: opts.ConfigF,
keystore: opts.KeyStore,
}
}
@@ -243,6 +244,13 @@ func (lmem *lockedMemRepo) Datastore(ns string) (datastore.Batching, error) {
return namespace.Wrap(lmem.mem.datastore, datastore.NewKey(ns)), nil
}
func (lmem *lockedMemRepo) Blockstore(domain BlockstoreDomain) (blockstore.Blockstore, error) {
if domain != BlockstoreChain {
return nil, ErrInvalidBlockstoreDomain
}
return lmem.mem.blockstore, nil
}
func (lmem *lockedMemRepo) ListDatastores(ns string) ([]int64, error) {
return nil, nil
}