2021-02-27 10:44:31 +00:00
|
|
|
package splitstore
|
|
|
|
|
|
|
|
import (
|
2021-07-04 03:34:26 +00:00
|
|
|
"sync"
|
2021-02-27 10:44:31 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
|
|
cid "github.com/ipfs/go-cid"
|
|
|
|
bolt "go.etcd.io/bbolt"
|
|
|
|
)
|
|
|
|
|
2021-07-04 03:34:26 +00:00
|
|
|
const boltMarkSetStaging = 16384
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
type BoltMarkSetEnv struct {
|
2021-02-27 10:44:31 +00:00
|
|
|
db *bolt.DB
|
|
|
|
}
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
var _ MarkSetEnv = (*BoltMarkSetEnv)(nil)
|
2021-02-27 10:44:31 +00:00
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
type BoltMarkSet struct {
|
2021-02-27 10:44:31 +00:00
|
|
|
db *bolt.DB
|
|
|
|
bucketId []byte
|
2021-07-04 03:34:26 +00:00
|
|
|
|
|
|
|
// cache for batching
|
|
|
|
mx sync.RWMutex
|
|
|
|
pend map[string]struct{}
|
2021-02-27 10:44:31 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
var _ MarkSet = (*BoltMarkSet)(nil)
|
2021-02-27 10:44:31 +00:00
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
func NewBoltMarkSetEnv(path string) (*BoltMarkSetEnv, error) {
|
2021-02-27 10:44:31 +00:00
|
|
|
db, err := bolt.Open(path, 0644,
|
|
|
|
&bolt.Options{
|
|
|
|
Timeout: 1 * time.Second,
|
2021-02-27 14:06:33 +00:00
|
|
|
NoSync: true,
|
2021-02-27 10:44:31 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
return &BoltMarkSetEnv{db: db}, nil
|
2021-02-27 10:44:31 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
func (e *BoltMarkSetEnv) Create(name string, hint int64) (MarkSet, error) {
|
2021-02-27 10:44:31 +00:00
|
|
|
bucketId := []byte(name)
|
|
|
|
err := e.db.Update(func(tx *bolt.Tx) error {
|
|
|
|
_, err := tx.CreateBucketIfNotExists(bucketId)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("error creating bolt db bucket %s: %w", name, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-07-04 03:34:26 +00:00
|
|
|
return &BoltMarkSet{
|
|
|
|
db: e.db,
|
|
|
|
bucketId: bucketId,
|
|
|
|
pend: make(map[string]struct{}),
|
|
|
|
}, nil
|
2021-02-27 10:44:31 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
func (e *BoltMarkSetEnv) Close() error {
|
2021-02-27 10:44:31 +00:00
|
|
|
return e.db.Close()
|
|
|
|
}
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
func (s *BoltMarkSet) Mark(cid cid.Cid) error {
|
2021-07-04 03:34:26 +00:00
|
|
|
s.mx.Lock()
|
|
|
|
defer s.mx.Unlock()
|
|
|
|
|
|
|
|
key := cid.Hash()
|
|
|
|
s.pend[string(key)] = struct{}{}
|
|
|
|
|
|
|
|
if len(s.pend) < boltMarkSetStaging {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err := s.db.Batch(func(tx *bolt.Tx) error {
|
2021-02-27 10:44:31 +00:00
|
|
|
b := tx.Bucket(s.bucketId)
|
2021-07-04 03:34:26 +00:00
|
|
|
for key := range s.pend {
|
|
|
|
err := b.Put([]byte(key), markBytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2021-02-27 10:44:31 +00:00
|
|
|
})
|
2021-07-04 03:34:26 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.pend = make(map[string]struct{})
|
|
|
|
return nil
|
2021-02-27 10:44:31 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
func (s *BoltMarkSet) Has(cid cid.Cid) (result bool, err error) {
|
2021-07-04 03:34:26 +00:00
|
|
|
s.mx.RLock()
|
|
|
|
defer s.mx.RUnlock()
|
|
|
|
|
|
|
|
key := cid.Hash()
|
|
|
|
_, result = s.pend[string(key)]
|
|
|
|
if result {
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2021-02-27 10:44:31 +00:00
|
|
|
err = s.db.View(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket(s.bucketId)
|
2021-07-04 03:34:26 +00:00
|
|
|
v := b.Get(key)
|
2021-02-27 10:44:31 +00:00
|
|
|
result = v != nil
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2021-03-02 00:47:21 +00:00
|
|
|
func (s *BoltMarkSet) Close() error {
|
2021-02-27 10:44:31 +00:00
|
|
|
return s.db.Update(func(tx *bolt.Tx) error {
|
|
|
|
return tx.DeleteBucket(s.bucketId)
|
|
|
|
})
|
|
|
|
}
|