2021-01-29 20:01:00 +00:00
|
|
|
package blockstore
|
|
|
|
|
|
|
|
import (
|
2021-11-19 01:50:25 +00:00
|
|
|
"context"
|
2023-03-04 15:23:02 +00:00
|
|
|
"time"
|
2021-11-19 01:50:25 +00:00
|
|
|
|
2023-05-25 14:31:53 +00:00
|
|
|
blockstore "github.com/ipfs/boxo/blockstore"
|
2022-06-15 10:06:22 +00:00
|
|
|
"github.com/ipfs/go-cid"
|
2021-01-29 20:01:00 +00:00
|
|
|
ds "github.com/ipfs/go-datastore"
|
2022-06-14 15:00:51 +00:00
|
|
|
logging "github.com/ipfs/go-log/v2"
|
2021-01-29 20:01:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var log = logging.Logger("blockstore")
|
|
|
|
|
|
|
|
// Blockstore is the blockstore interface used by Lotus. It is the union
|
|
|
|
// of the basic go-ipfs blockstore, with other capabilities required by Lotus,
|
|
|
|
// e.g. View or Sync.
|
|
|
|
type Blockstore interface {
|
|
|
|
blockstore.Blockstore
|
|
|
|
blockstore.Viewer
|
2021-03-02 14:45:45 +00:00
|
|
|
BatchDeleter
|
2020-11-24 07:11:28 +00:00
|
|
|
Flusher
|
2021-01-29 20:01:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BasicBlockstore is an alias to the original IPFS Blockstore.
|
|
|
|
type BasicBlockstore = blockstore.Blockstore
|
|
|
|
|
|
|
|
type Viewer = blockstore.Viewer
|
|
|
|
|
2020-11-24 07:11:28 +00:00
|
|
|
type Flusher interface {
|
|
|
|
Flush(context.Context) error
|
|
|
|
}
|
|
|
|
|
2021-03-02 14:45:45 +00:00
|
|
|
type BatchDeleter interface {
|
2021-11-19 01:50:25 +00:00
|
|
|
DeleteMany(ctx context.Context, cids []cid.Cid) error
|
2021-03-02 14:45:45 +00:00
|
|
|
}
|
|
|
|
|
2021-07-04 05:43:23 +00:00
|
|
|
// BlockstoreIterator is a trait for efficient iteration
|
|
|
|
type BlockstoreIterator interface {
|
|
|
|
ForEachKey(func(cid.Cid) error) error
|
|
|
|
}
|
|
|
|
|
2021-07-11 05:37:31 +00:00
|
|
|
// BlockstoreGC is a trait for blockstores that support online garbage collection
|
|
|
|
type BlockstoreGC interface {
|
2023-03-03 16:14:52 +00:00
|
|
|
CollectGarbage(ctx context.Context, options ...BlockstoreGCOption) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// BlockstoreGCOnce is a trait for a blockstore that supports incremental online garbage collection
|
|
|
|
type BlockstoreGCOnce interface {
|
|
|
|
GCOnce(ctx context.Context, options ...BlockstoreGCOption) error
|
2021-07-11 05:37:31 +00:00
|
|
|
}
|
|
|
|
|
2021-07-27 06:50:44 +00:00
|
|
|
// BlockstoreGCOption is a functional interface for controlling blockstore GC options
|
|
|
|
type BlockstoreGCOption = func(*BlockstoreGCOptions) error
|
2021-07-23 19:30:40 +00:00
|
|
|
|
2021-07-27 06:50:44 +00:00
|
|
|
// BlockstoreGCOptions is a struct with GC options
|
|
|
|
type BlockstoreGCOptions struct {
|
|
|
|
FullGC bool
|
2023-03-03 16:14:52 +00:00
|
|
|
// fraction of garbage in badger vlog before its worth processing in online GC
|
|
|
|
Threshold float64
|
2023-03-04 15:23:02 +00:00
|
|
|
// how often to call the check function
|
|
|
|
CheckFreq time.Duration
|
|
|
|
// function to call periodically to pause or early terminate GC
|
|
|
|
Check func() error
|
2021-07-27 06:50:44 +00:00
|
|
|
}
|
2021-07-23 19:30:40 +00:00
|
|
|
|
2021-07-27 06:50:44 +00:00
|
|
|
func WithFullGC(fullgc bool) BlockstoreGCOption {
|
|
|
|
return func(opts *BlockstoreGCOptions) error {
|
|
|
|
opts.FullGC = fullgc
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2021-07-23 19:30:40 +00:00
|
|
|
|
2023-03-03 16:14:52 +00:00
|
|
|
func WithThreshold(threshold float64) BlockstoreGCOption {
|
|
|
|
return func(opts *BlockstoreGCOptions) error {
|
|
|
|
opts.Threshold = threshold
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-04 15:23:02 +00:00
|
|
|
func WithCheckFreq(f time.Duration) BlockstoreGCOption {
|
|
|
|
return func(opts *BlockstoreGCOptions) error {
|
|
|
|
opts.CheckFreq = f
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func WithCheck(check func() error) BlockstoreGCOption {
|
|
|
|
return func(opts *BlockstoreGCOptions) error {
|
|
|
|
opts.Check = check
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-26 05:42:54 +00:00
|
|
|
// BlockstoreSize is a trait for on-disk blockstores that can report their size
|
|
|
|
type BlockstoreSize interface {
|
|
|
|
Size() (int64, error)
|
|
|
|
}
|
|
|
|
|
2021-01-29 20:01:00 +00:00
|
|
|
// WrapIDStore wraps the underlying blockstore in an "identity" blockstore.
|
2021-01-29 23:24:44 +00:00
|
|
|
// The ID store filters out all puts for blocks with CIDs using the "identity"
|
|
|
|
// hash function. It also extracts inlined blocks from CIDs using the identity
|
|
|
|
// hash function and returns them on get/has, ignoring the contents of the
|
|
|
|
// blockstore.
|
2021-01-29 20:01:00 +00:00
|
|
|
func WrapIDStore(bstore blockstore.Blockstore) Blockstore {
|
2021-03-02 16:06:19 +00:00
|
|
|
if is, ok := bstore.(*idstore); ok {
|
|
|
|
// already wrapped
|
|
|
|
return is
|
|
|
|
}
|
|
|
|
|
|
|
|
if bs, ok := bstore.(Blockstore); ok {
|
2021-03-02 16:19:20 +00:00
|
|
|
// we need to wrap our own because we don't want to neuter the DeleteMany method
|
|
|
|
// the underlying blockstore has implemented an (efficient) DeleteMany
|
2021-03-02 16:06:19 +00:00
|
|
|
return NewIDStore(bs)
|
|
|
|
}
|
|
|
|
|
2021-03-02 16:21:17 +00:00
|
|
|
// The underlying blockstore does not implement DeleteMany, so we need to shim it.
|
|
|
|
// This is less efficient as it'll iterate and perform single deletes.
|
2021-03-02 16:06:19 +00:00
|
|
|
return NewIDStore(Adapt(bstore))
|
2021-01-29 20:01:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FromDatastore creates a new blockstore backed by the given datastore.
|
|
|
|
func FromDatastore(dstore ds.Batching) Blockstore {
|
2021-03-02 16:21:17 +00:00
|
|
|
return WrapIDStore(blockstore.NewBlockstore(dstore))
|
2021-01-29 20:01:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type adaptedBlockstore struct {
|
|
|
|
blockstore.Blockstore
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Blockstore = (*adaptedBlockstore)(nil)
|
|
|
|
|
2020-11-24 07:11:28 +00:00
|
|
|
func (a *adaptedBlockstore) Flush(ctx context.Context) error {
|
|
|
|
if flusher, canFlush := a.Blockstore.(Flusher); canFlush {
|
|
|
|
return flusher.Flush(ctx)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-11-19 01:50:25 +00:00
|
|
|
func (a *adaptedBlockstore) View(ctx context.Context, cid cid.Cid, callback func([]byte) error) error {
|
|
|
|
blk, err := a.Get(ctx, cid)
|
2021-01-29 20:01:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return callback(blk.RawData())
|
|
|
|
}
|
|
|
|
|
2021-11-19 01:50:25 +00:00
|
|
|
func (a *adaptedBlockstore) DeleteMany(ctx context.Context, cids []cid.Cid) error {
|
2021-03-02 14:45:45 +00:00
|
|
|
for _, cid := range cids {
|
2021-11-19 01:50:25 +00:00
|
|
|
err := a.DeleteBlock(ctx, cid)
|
2021-03-02 14:45:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-29 20:01:00 +00:00
|
|
|
// Adapt adapts a standard blockstore to a Lotus blockstore by
|
|
|
|
// enriching it with the extra methods that Lotus requires (e.g. View, Sync).
|
|
|
|
//
|
|
|
|
// View proxies over to Get and calls the callback with the value supplied by Get.
|
|
|
|
// Sync noops.
|
|
|
|
func Adapt(bs blockstore.Blockstore) Blockstore {
|
2021-02-28 19:44:02 +00:00
|
|
|
if ret, ok := bs.(Blockstore); ok {
|
|
|
|
return ret
|
|
|
|
}
|
2021-01-29 20:01:00 +00:00
|
|
|
return &adaptedBlockstore{bs}
|
|
|
|
}
|