diff --git a/chain/store/splitstore/snoop.go b/chain/store/splitstore/snoop.go index 08faabd6c..3a2b5f43e 100644 --- a/chain/store/splitstore/snoop.go +++ b/chain/store/splitstore/snoop.go @@ -1,7 +1,6 @@ package splitstore import ( - "fmt" "path/filepath" "github.com/filecoin-project/go-state-types/abi" @@ -22,5 +21,5 @@ func NewTrackingStore(path string, useLMDB bool) (TrackingStore, error) { return NewLMDBTrackingStore(filepath.Join(path, "snoop.lmdb")) } - return nil, fmt.Errorf("TODO: non-lmdb livesets") + return NewBoltTrackingStore(filepath.Join(path, "snoop.bolt")) } diff --git a/chain/store/splitstore/snoop_bolt.go b/chain/store/splitstore/snoop_bolt.go new file mode 100644 index 000000000..015a11838 --- /dev/null +++ b/chain/store/splitstore/snoop_bolt.go @@ -0,0 +1,102 @@ +package splitstore + +import ( + "time" + + "golang.org/x/xerrors" + + cid "github.com/ipfs/go-cid" + bolt "go.etcd.io/bbolt" + + "github.com/filecoin-project/go-state-types/abi" +) + +type BoltTrackingStore struct { + db *bolt.DB + bucketId []byte +} + +var _ TrackingStore = (*BoltTrackingStore)(nil) + +func NewBoltTrackingStore(path string) (*BoltTrackingStore, error) { + db, err := bolt.Open(path, 0644, + &bolt.Options{ + Timeout: 1 * time.Second, + }) + if err != nil { + return nil, err + } + + bucketId := []byte("snoop") + err = db.Update(func(tx *bolt.Tx) error { + _, err := tx.CreateBucketIfNotExists(bucketId) + if err != nil { + return xerrors.Errorf("error creating bolt db bucket %s: %w", string(bucketId), err) + } + return nil + }) + + if err != nil { + db.Close() //nolint:errcheck + return nil, err + } + + return &BoltTrackingStore{db: db, bucketId: bucketId}, nil +} + +func (s *BoltTrackingStore) Put(cid cid.Cid, epoch abi.ChainEpoch) error { + val := epochToBytes(epoch) + return s.db.Batch(func(tx *bolt.Tx) error { + b := tx.Bucket(s.bucketId) + return b.Put(cid.Hash(), val) + }) +} + +func (s *BoltTrackingStore) PutBatch(cids []cid.Cid, epoch abi.ChainEpoch) error { + val := epochToBytes(epoch) + return s.db.Batch(func(tx *bolt.Tx) error { + b := tx.Bucket(s.bucketId) + for _, cid := range cids { + err := b.Put(cid.Hash(), val) + if err != nil { + return err + } + } + return nil + }) +} + +func (s *BoltTrackingStore) Get(cid cid.Cid) (epoch abi.ChainEpoch, err error) { + err = s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket(s.bucketId) + val := b.Get(cid.Hash()) + if val == nil { + return xerrors.Errorf("missing tracking epoch for %s", cid) + } + epoch = bytesToEpoch(val) + return nil + }) + return epoch, err +} + +func (s *BoltTrackingStore) Delete(cid cid.Cid) error { + return s.db.Batch(func(tx *bolt.Tx) error { + b := tx.Bucket(s.bucketId) + return b.Delete(cid.Hash()) + }) +} + +func (s *BoltTrackingStore) ForEach(f func(cid.Cid, abi.ChainEpoch) error) error { + return s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket(s.bucketId) + return b.ForEach(func(k, v []byte) error { + cid := cid.NewCidV1(cid.Raw, k) + epoch := bytesToEpoch(v) + return f(cid, epoch) + }) + }) +} + +func (s *BoltTrackingStore) Close() error { + return s.db.Close() +} diff --git a/chain/store/splitstore/snoop_lmdb.go b/chain/store/splitstore/snoop_lmdb.go index 21d596bfb..1eca73feb 100644 --- a/chain/store/splitstore/snoop_lmdb.go +++ b/chain/store/splitstore/snoop_lmdb.go @@ -20,7 +20,7 @@ type LMDBTrackingStore struct { var _ TrackingStore = (*LMDBTrackingStore)(nil) -func NewLMDBTrackingStore(path string) (TrackingStore, error) { +func NewLMDBTrackingStore(path string) (*LMDBTrackingStore, error) { env, err := lmdb.NewEnv() if err != nil { return nil, xerrors.Errorf("failed to initialize LMDB env: %w", err)