Skip to content

Commit

Permalink
fix a race in tipset cache usage
Browse files Browse the repository at this point in the history
This tipset cache is shared between multiple services and is called from
multiple places.
  • Loading branch information
Stebalien committed Oct 10, 2020
1 parent 83ba7ec commit 2860055
Showing 1 changed file with 19 additions and 1 deletion.
20 changes: 19 additions & 1 deletion chain/events/tscache.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package events

import (
"context"
"sync"

"github.com/filecoin-project/go-state-types/abi"
"golang.org/x/xerrors"
Expand All @@ -17,6 +18,8 @@ type tsCacheAPI interface {
// tipSetCache implements a simple ring-buffer cache to keep track of recent
// tipsets
type tipSetCache struct {
mu sync.RWMutex

cache []*types.TipSet
start int
len int
Expand All @@ -35,6 +38,9 @@ func newTSCache(cap abi.ChainEpoch, storage tsCacheAPI) *tipSetCache {
}

func (tsc *tipSetCache) add(ts *types.TipSet) error {
tsc.mu.Lock()
defer tsc.mu.Unlock()

if tsc.len > 0 {
if tsc.cache[tsc.start].Height() >= ts.Height() {
return xerrors.Errorf("tipSetCache.add: expected new tipset height to be at least %d, was %d", tsc.cache[tsc.start].Height()+1, ts.Height())
Expand Down Expand Up @@ -65,6 +71,9 @@ func (tsc *tipSetCache) add(ts *types.TipSet) error {
}

func (tsc *tipSetCache) revert(ts *types.TipSet) error {
tsc.mu.Lock()
defer tsc.mu.Unlock()

if tsc.len == 0 {
return nil // this can happen, and it's fine
}
Expand Down Expand Up @@ -95,14 +104,18 @@ func (tsc *tipSetCache) getNonNull(height abi.ChainEpoch) (*types.TipSet, error)
}

func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) {
tsc.mu.RLock()

if tsc.len == 0 {
tsc.mu.RUnlock()
log.Warnf("tipSetCache.get: cache is empty, requesting from storage (h=%d)", height)
return tsc.storage.ChainGetTipSetByHeight(context.TODO(), height, types.EmptyTSK)
}

headH := tsc.cache[tsc.start].Height()

if height > headH {
tsc.mu.RUnlock()
return nil, xerrors.Errorf("tipSetCache.get: requested tipset not in cache (req: %d, cache head: %d)", height, headH)
}

Expand All @@ -116,15 +129,20 @@ func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) {
}

if height < tail.Height() {
tsc.mu.RUnlock()
log.Warnf("tipSetCache.get: requested tipset not in cache, requesting from storage (h=%d; tail=%d)", height, tail.Height())
return tsc.storage.ChainGetTipSetByHeight(context.TODO(), height, tail.Key())
}

return tsc.cache[normalModulo(tsc.start-int(headH-height), clen)], nil
ts := tsc.cache[normalModulo(tsc.start-int(headH-height), clen)]
tsc.mu.RUnlock()
return ts, nil
}

func (tsc *tipSetCache) best() (*types.TipSet, error) {
tsc.mu.RLock()
best := tsc.cache[tsc.start]
tsc.mu.RUnlock()
if best == nil {
return tsc.storage.ChainHead(context.TODO())
}
Expand Down

0 comments on commit 2860055

Please sign in to comment.