get rid of goroutine iteration in tracking store; long live ForEach

This commit is contained in:
vyzo 2021-02-26 15:59:36 +02:00
parent a586d42c3b
commit 842ec43c2f
2 changed files with 47 additions and 72 deletions

View File

@ -1,7 +1,6 @@
package splitstore
import (
"context"
"os"
"golang.org/x/xerrors"
@ -20,7 +19,7 @@ type TrackingStore interface {
PutBatch([]cid.Cid, abi.ChainEpoch) error
Get(cid.Cid) (abi.ChainEpoch, error)
Delete(cid.Cid) error
Keys(context.Context) (<-chan cid.Cid, error)
ForEach(func(cid.Cid, abi.ChainEpoch) error) error
Close() error
}
@ -40,9 +39,6 @@ func NewTrackingStore(path string) (TrackingStore, error) {
if err = env.SetMaxDBs(1); err != nil {
return nil, xerrors.Errorf("failed to set LMDB max dbs: %w", err)
}
// if err = env.SetMaxReaders(2); err != nil {
// return nil, xerrors.Errorf("failed to set LMDB max readers: %w", err)
// }
if st, err := os.Stat(path); os.IsNotExist(err) {
if err := os.MkdirAll(path, 0777); err != nil {
@ -129,47 +125,37 @@ func (s *trackingStore) Delete(cid cid.Cid) error {
})
}
func (s *trackingStore) Keys(ctx context.Context) (<-chan cid.Cid, error) {
ch := make(chan cid.Cid)
go func() {
defer close(ch)
func (s *trackingStore) ForEach(f func(cid.Cid, abi.ChainEpoch) error) error {
return withMaxReadersRetry(
func() error {
return s.env.View(func(txn *lmdb.Txn) error {
txn.RawRead = true
cur, err := txn.OpenCursor(s.db)
if err != nil {
return err
}
defer cur.Close()
err := withMaxReadersRetry(
func() error {
return s.env.View(func(txn *lmdb.Txn) error {
for {
k, v, err := cur.Get(nil, nil, lmdb.Next)
if err != nil {
if lmdb.IsNotFound(err) {
return nil
}
txn.RawRead = true
cur, err := txn.OpenCursor(s.db)
return err
}
cid := cid.NewCidV1(cid.Raw, k)
epoch := bytesToEpoch(v)
err = f(cid, epoch)
if err != nil {
return err
}
defer cur.Close()
for {
k, _, err := cur.Get(nil, nil, lmdb.Next)
if err != nil {
if lmdb.IsNotFound(err) {
return nil
}
return err
}
select {
case ch <- cid.NewCidV1(cid.Raw, k):
case <-ctx.Done():
return nil
}
}
})
}
})
if err != nil {
log.Errorf("error iterating over tracking store keys: %s", err)
}
}()
return ch, nil
})
}
func (s *trackingStore) Close() error {

View File

@ -9,6 +9,8 @@ import (
"sync/atomic"
"time"
"golang.org/x/xerrors"
"github.com/ledgerwatch/lmdb-go/lmdb"
blocks "github.com/ipfs/go-block-format"
@ -369,51 +371,37 @@ func (s *SplitStore) compact() {
// Phase 2: sweep cold objects:
// - If a cold object is reachable in the hot range, it stays in the hotstore.
// - If a cold object is reachable in the cold range, it is moved to the coldstore.
// - If a cold object is unreachable, it is deleted.
ch, err := s.snoop.Keys(context.Background())
if err != nil {
// TODO do something better here
panic(err)
}
// - If a cold object is unreachable, it is deleted if GC is enabled, otherwise moved to the coldstore.
startSweep := time.Now()
log.Info("sweeping cold objects")
// some stats for logging
var stHot, stCold, stDead int
for cid := range ch {
wrEpoch, err := s.snoop.Get(cid)
if err != nil {
// TODO do something better here
panic(err)
}
err = s.snoop.ForEach(func(cid cid.Cid, wrEpoch abi.ChainEpoch) error {
// is the object stil hot?
if wrEpoch > coldEpoch {
// yes, stay in the hotstore
stHot++
continue
return nil
}
// the object is cold -- check whether it is reachable in the hot range
mark, err := hotSet.Has(cid)
if err != nil {
// TODO do something better here
panic(err)
return xerrors.Errorf("error checking live mark for %s: %w", cid, err)
}
if mark {
// the object is reachable in the hot range, stay in the hotstore
stHot++
continue
return nil
}
// check whether it is reachable in the cold range
mark, err = coldSet.Has(cid)
if err != nil {
// TODO do something better here
panic(err)
return xerrors.Errorf("error checkiing cold set for %s: %w", cid, err)
}
if s.enableGC {
@ -421,14 +409,12 @@ func (s *SplitStore) compact() {
// the object is reachable in the cold range, move it to the cold store
blk, err := s.hot.Get(cid)
if err != nil {
// TODO do something better here
panic(err)
return xerrors.Errorf("error retrieving tracked block %s from hotstore: %w ", cid, err)
}
err = s.cold.Put(blk)
if err != nil {
// TODO do something better here
panic(err)
return xerrors.Errorf("error puting block %s to coldstore: %w", cid, err)
}
stCold++
@ -440,14 +426,12 @@ func (s *SplitStore) compact() {
// if GC is disabled, we move both cold and dead objects to the coldstore
blk, err := s.hot.Get(cid)
if err != nil {
// TODO do something better here
panic(err)
return xerrors.Errorf("error retrieving tracked block %s from hotstore: %w ", cid, err)
}
err = s.cold.Put(blk)
if err != nil {
// TODO do something better here
panic(err)
return xerrors.Errorf("error puting block %s to coldstore: %w", cid, err)
}
if mark {
@ -460,16 +444,21 @@ func (s *SplitStore) compact() {
// delete the object from the hotstore
err = s.hot.DeleteBlock(cid)
if err != nil {
// TODO do something better here
panic(err)
return xerrors.Errorf("error deleting block %s from hotstore: %w", cid, err)
}
// remove the snoop tracking
err = s.snoop.Delete(cid)
if err != nil {
// TODO do something better here
panic(err)
return xerrors.Errorf("error deleting cid %s from tracking store: %w", cid, err)
}
return nil
})
if err != nil {
// TODO do something better here
panic(err)
}
log.Infow("sweeping done", "took", time.Since(startSweep))