Skip to content
This repository was archived by the owner on Jun 19, 2023. It is now read-only.

Commit 8d1f7bf

Browse files
committed
fix(arc): striped locking on last byte of CID
- fixes #64
1 parent fb07d7b commit 8d1f7bf

File tree

2 files changed

+95
-13
lines changed

2 files changed

+95
-13
lines changed

arc_cache.go

+67-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package blockstore
22

33
import (
44
"context"
5+
"sync"
56

67
lru "github.com/hashicorp/golang-lru"
78
blocks "github.com/ipfs/go-block-format"
@@ -17,7 +18,9 @@ type cacheSize int
1718
// size. This provides block access-time improvements, allowing
1819
// to short-cut many searches without querying the underlying datastore.
1920
type arccache struct {
20-
cache *lru.TwoQueueCache
21+
cache *lru.TwoQueueCache
22+
lks [256]sync.RWMutex
23+
2124
blockstore Blockstore
2225
viewer Viewer
2326

@@ -42,11 +45,27 @@ func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache,
4245
return c, nil
4346
}
4447

48+
func (b *arccache) getLock(k cid.Cid) *sync.RWMutex {
49+
return &b.lks[mutexKey(k)]
50+
}
51+
52+
func mutexKey(k cid.Cid) uint8 {
53+
return k.KeyString()[len(k.KeyString())-1]
54+
}
55+
4556
func (b *arccache) DeleteBlock(k cid.Cid) error {
57+
if !k.Defined() {
58+
return nil
59+
}
60+
4661
if has, _, ok := b.queryCache(k); ok && !has {
4762
return nil
4863
}
4964

65+
lk := b.getLock(k)
66+
lk.Lock()
67+
defer lk.Unlock()
68+
5069
b.cache.Remove(k) // Invalidate cache before deleting.
5170
err := b.blockstore.DeleteBlock(k)
5271
if err == nil {
@@ -56,9 +75,18 @@ func (b *arccache) DeleteBlock(k cid.Cid) error {
5675
}
5776

5877
func (b *arccache) Has(k cid.Cid) (bool, error) {
78+
if !k.Defined() {
79+
return false, nil
80+
}
81+
5982
if has, _, ok := b.queryCache(k); ok {
6083
return has, nil
6184
}
85+
86+
lk := b.getLock(k)
87+
lk.RLock()
88+
defer lk.RUnlock()
89+
6290
has, err := b.blockstore.Has(k)
6391
if err != nil {
6492
return false, err
@@ -68,6 +96,10 @@ func (b *arccache) Has(k cid.Cid) (bool, error) {
6896
}
6997

7098
func (b *arccache) GetSize(k cid.Cid) (int, error) {
99+
if !k.Defined() {
100+
return -1, ErrNotFound
101+
}
102+
71103
if has, blockSize, ok := b.queryCache(k); ok {
72104
if !has {
73105
// don't have it, return
@@ -79,6 +111,11 @@ func (b *arccache) GetSize(k cid.Cid) (int, error) {
79111
}
80112
// we have it but don't know the size, ask the datastore.
81113
}
114+
115+
lk := b.getLock(k)
116+
lk.RLock()
117+
defer lk.RUnlock()
118+
82119
blockSize, err := b.blockstore.GetSize(k)
83120
if err == ErrNotFound {
84121
b.cacheHave(k, false)
@@ -100,7 +137,6 @@ func (b *arccache) View(k cid.Cid, callback func([]byte) error) error {
100137
}
101138

102139
if !k.Defined() {
103-
log.Error("undefined cid in arc cache")
104140
return ErrNotFound
105141
}
106142

@@ -110,19 +146,26 @@ func (b *arccache) View(k cid.Cid, callback func([]byte) error) error {
110146
return ErrNotFound
111147
}
112148

149+
lk := b.getLock(k)
150+
lk.RLock()
151+
defer lk.RUnlock()
152+
113153
return b.viewer.View(k, callback)
114154
}
115155

116156
func (b *arccache) Get(k cid.Cid) (blocks.Block, error) {
117157
if !k.Defined() {
118-
log.Error("undefined cid in arc cache")
119158
return nil, ErrNotFound
120159
}
121160

122161
if has, _, ok := b.queryCache(k); ok && !has {
123162
return nil, ErrNotFound
124163
}
125164

165+
lk := b.getLock(k)
166+
lk.RLock()
167+
defer lk.RUnlock()
168+
126169
bl, err := b.blockstore.Get(k)
127170
if bl == nil && err == ErrNotFound {
128171
b.cacheHave(k, false)
@@ -137,6 +180,10 @@ func (b *arccache) Put(bl blocks.Block) error {
137180
return nil
138181
}
139182

183+
lk := b.getLock(bl.Cid())
184+
lk.Lock()
185+
defer lk.Unlock()
186+
140187
err := b.blockstore.Put(bl)
141188
if err == nil {
142189
b.cacheSize(bl.Cid(), len(bl.RawData()))
@@ -145,14 +192,31 @@ func (b *arccache) Put(bl blocks.Block) error {
145192
}
146193

147194
func (b *arccache) PutMany(bs []blocks.Block) error {
195+
mxs := [256]*sync.RWMutex{}
148196
var good []blocks.Block
149197
for _, block := range bs {
150198
// call put on block if result is inconclusive or we are sure that
151199
// the block isn't in storage
152200
if has, _, ok := b.queryCache(block.Cid()); !ok || (ok && !has) {
153201
good = append(good, block)
202+
mxs[mutexKey(block.Cid())] = &b.lks[mutexKey(block.Cid())]
203+
}
204+
}
205+
206+
for _, mx := range mxs {
207+
if mx != nil {
208+
mx.Lock()
154209
}
155210
}
211+
212+
defer func() {
213+
for _, mx := range mxs {
214+
if mx != nil {
215+
mx.Unlock()
216+
}
217+
}
218+
}()
219+
156220
err := b.blockstore.PutMany(good)
157221
if err != nil {
158222
return err

arc_cache_test.go

+28-10
Original file line numberDiff line numberDiff line change
@@ -246,16 +246,34 @@ func TestDifferentKeyObjectsWork(t *testing.T) {
246246
}
247247

248248
func TestPutManyCaches(t *testing.T) {
249-
arc, _, cd := createStores(t)
250-
arc.PutMany([]blocks.Block{exampleBlock})
249+
t.Run("happy path PutMany", func(t *testing.T) {
250+
arc, _, cd := createStores(t)
251+
arc.PutMany([]blocks.Block{exampleBlock})
252+
253+
trap("has hit datastore", cd, t)
254+
arc.Has(exampleBlock.Cid())
255+
arc.GetSize(exampleBlock.Cid())
256+
untrap(cd)
257+
arc.DeleteBlock(exampleBlock.Cid())
258+
259+
arc.Put(exampleBlock)
260+
trap("PunMany has hit datastore", cd, t)
261+
arc.PutMany([]blocks.Block{exampleBlock})
262+
})
251263

252-
trap("has hit datastore", cd, t)
253-
arc.Has(exampleBlock.Cid())
254-
arc.GetSize(exampleBlock.Cid())
255-
untrap(cd)
256-
arc.DeleteBlock(exampleBlock.Cid())
264+
t.Run("PutMany with duplicates", func(t *testing.T) {
265+
arc, _, cd := createStores(t)
266+
arc.PutMany([]blocks.Block{exampleBlock, exampleBlock})
267+
268+
trap("has hit datastore", cd, t)
269+
arc.Has(exampleBlock.Cid())
270+
arc.GetSize(exampleBlock.Cid())
271+
untrap(cd)
272+
arc.DeleteBlock(exampleBlock.Cid())
273+
274+
arc.Put(exampleBlock)
275+
trap("PunMany has hit datastore", cd, t)
276+
arc.PutMany([]blocks.Block{exampleBlock})
277+
})
257278

258-
arc.Put(exampleBlock)
259-
trap("PunMany has hit datastore", cd, t)
260-
arc.PutMany([]blocks.Block{exampleBlock})
261279
}

0 commit comments

Comments
 (0)