From 9b8dea9951dbffe934d0cbdb84ec2b47db2636d8 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Mon, 19 Jun 2023 16:59:13 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20golang-lru=20=E2=86=92=20golang-lru?= =?UTF-8?q?/v2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + remove mentions of ARC Closes #366 --- CHANGELOG.md | 2 + blockstore/bloom_cache_test.go | 2 +- blockstore/caching.go | 12 +- blockstore/caching_test.go | 2 +- .../{arc_cache.go => twoqueue_cache.go} | 63 ++++--- ...c_cache_test.go => twoqueue_cache_test.go} | 172 +++++++++--------- examples/go.mod | 1 + examples/go.sum | 2 + go.mod | 3 +- go.sum | 2 + namesys/namesys.go | 6 +- 11 files changed, 139 insertions(+), 128 deletions(-) rename blockstore/{arc_cache.go => twoqueue_cache.go} (77%) rename blockstore/{arc_cache_test.go => twoqueue_cache_test.go} (61%) diff --git a/CHANGELOG.md b/CHANGELOG.md index f160fb3d7..8618b7103 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ The following emojis are used to highlight certain changes: ### Fixed +- Removed mentions of unused ARC algorithm ([#336](https://github.com/ipfs/boxo/issues/366#issuecomment-1597253540)) + ### Security ## [0.10.1] - 2023-06-19 diff --git a/blockstore/bloom_cache_test.go b/blockstore/bloom_cache_test.go index 3c998c551..70d118e09 100644 --- a/blockstore/bloom_cache_test.go +++ b/blockstore/bloom_cache_test.go @@ -21,7 +21,7 @@ func testBloomCached(ctx context.Context, bs Blockstore) (*bloomcache, error) { ctx = context.Background() } opts := DefaultCacheOpts() - opts.HasARCCacheSize = 0 + opts.HasTwoQueueCacheSize = 0 bbs, err := CachedBlockstore(ctx, bs, opts) if err == nil { return bbs.(*bloomcache), nil diff --git a/blockstore/caching.go b/blockstore/caching.go index 798b84ce2..bfbe449a9 100644 --- a/blockstore/caching.go +++ b/blockstore/caching.go @@ -12,7 +12,7 @@ import ( type CacheOpts struct { HasBloomFilterSize int // 1 byte HasBloomFilterHashes int // No size, 7 is usually best, consult bloom papers - HasARCCacheSize int // 32 bytes + HasTwoQueueCacheSize int // 32 bytes } // DefaultCacheOpts returns a CacheOpts initialized with default values. @@ -20,11 +20,11 @@ func DefaultCacheOpts() CacheOpts { return CacheOpts{ HasBloomFilterSize: 512 << 10, HasBloomFilterHashes: 7, - HasARCCacheSize: 64 << 10, + HasTwoQueueCacheSize: 64 << 10, } } -// CachedBlockstore returns a blockstore wrapped in an ARCCache and +// CachedBlockstore returns a blockstore wrapped in an TwoQueueCache and // then in a bloom filter cache, if the options indicate it. func CachedBlockstore( ctx context.Context, @@ -33,7 +33,7 @@ func CachedBlockstore( cbs = bs if opts.HasBloomFilterSize < 0 || opts.HasBloomFilterHashes < 0 || - opts.HasARCCacheSize < 0 { + opts.HasTwoQueueCacheSize < 0 { return nil, errors.New("all options for cache need to be greater than zero") } @@ -43,8 +43,8 @@ func CachedBlockstore( ctx = metrics.CtxSubScope(ctx, "bs.cache") - if opts.HasARCCacheSize > 0 { - cbs, err = newARCCachedBS(ctx, cbs, opts.HasARCCacheSize) + if opts.HasTwoQueueCacheSize > 0 { + cbs, err = newTwoQueueCachedBS(ctx, cbs, opts.HasTwoQueueCacheSize) } if opts.HasBloomFilterSize != 0 { // *8 because of bytes to bits conversion diff --git a/blockstore/caching_test.go b/blockstore/caching_test.go index 16066ad18..af752456b 100644 --- a/blockstore/caching_test.go +++ b/blockstore/caching_test.go @@ -7,7 +7,7 @@ import ( func TestCachingOptsLessThanZero(t *testing.T) { opts := DefaultCacheOpts() - opts.HasARCCacheSize = -1 + opts.HasTwoQueueCacheSize = -1 if _, err := CachedBlockstore(context.TODO(), nil, opts); err == nil { t.Error("wrong ARC setting was not detected") diff --git a/blockstore/arc_cache.go b/blockstore/twoqueue_cache.go similarity index 77% rename from blockstore/arc_cache.go rename to blockstore/twoqueue_cache.go index ba8ecbb63..97374ad5b 100644 --- a/blockstore/arc_cache.go +++ b/blockstore/twoqueue_cache.go @@ -5,7 +5,7 @@ import ( "sort" "sync" - lru "github.com/hashicorp/golang-lru" + lru "github.com/hashicorp/golang-lru/v2" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" @@ -20,15 +20,17 @@ type lock struct { refcnt int } -// arccache wraps a BlockStore with an Adaptive Replacement Cache (ARC) that +// tqcache wraps a BlockStore with an [TwoQueueCache] that // does not store the actual blocks, just metadata about them: existence and // size. This provides block access-time improvements, allowing // to short-cut many searches without querying the underlying datastore. -type arccache struct { +// +// [TwoQueueCache]: https://pkg.go.dev/github.com/hashicorp/golang-lru/v2#TwoQueueCache +type tqcache struct { lklk sync.Mutex lks map[string]*lock - cache *lru.TwoQueueCache + cache *lru.TwoQueueCache[string, any] blockstore Blockstore viewer Viewer @@ -37,24 +39,25 @@ type arccache struct { total metrics.Counter } -var _ Blockstore = (*arccache)(nil) -var _ Viewer = (*arccache)(nil) +var _ Blockstore = (*tqcache)(nil) +var _ Viewer = (*tqcache)(nil) -func newARCCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*arccache, error) { - cache, err := lru.New2Q(lruSize) +func newTwoQueueCachedBS(ctx context.Context, bs Blockstore, lruSize int) (*tqcache, error) { + cache, err := lru.New2Q[string, any](lruSize) if err != nil { return nil, err } - c := &arccache{cache: cache, blockstore: bs, lks: make(map[string]*lock)} - c.hits = metrics.NewCtx(ctx, "arc.hits_total", "Number of ARC cache hits").Counter() - c.total = metrics.NewCtx(ctx, "arc_total", "Total number of ARC cache requests").Counter() + + c := &tqcache{cache: cache, blockstore: bs, lks: make(map[string]*lock)} + c.hits = metrics.NewCtx(ctx, "boxo_blockstore.cache_hits", "Number of blockstore cache hits").Counter() + c.total = metrics.NewCtx(ctx, "boxo_blockstore.cache_total", "Total number of blockstore cache requests").Counter() if v, ok := bs.(Viewer); ok { c.viewer = v } return c, nil } -func (b *arccache) lock(k string, write bool) { +func (b *tqcache) lock(k string, write bool) { b.lklk.Lock() lk, ok := b.lks[k] if !ok { @@ -70,7 +73,7 @@ func (b *arccache) lock(k string, write bool) { } } -func (b *arccache) unlock(key string, write bool) { +func (b *tqcache) unlock(key string, write bool) { b.lklk.Lock() lk := b.lks[key] lk.refcnt-- @@ -89,7 +92,7 @@ func cacheKey(k cid.Cid) string { return string(k.Hash()) } -func (b *arccache) DeleteBlock(ctx context.Context, k cid.Cid) error { +func (b *tqcache) DeleteBlock(ctx context.Context, k cid.Cid) error { if !k.Defined() { return nil } @@ -112,9 +115,9 @@ func (b *arccache) DeleteBlock(ctx context.Context, k cid.Cid) error { return err } -func (b *arccache) Has(ctx context.Context, k cid.Cid) (bool, error) { +func (b *tqcache) Has(ctx context.Context, k cid.Cid) (bool, error) { if !k.Defined() { - logger.Error("undefined cid in arccache") + logger.Error("undefined cid in tqcache") // Return cache invalid so the call to blockstore happens // in case of invalid key and correct error is created. return false, nil @@ -137,7 +140,7 @@ func (b *arccache) Has(ctx context.Context, k cid.Cid) (bool, error) { return has, nil } -func (b *arccache) GetSize(ctx context.Context, k cid.Cid) (int, error) { +func (b *tqcache) GetSize(ctx context.Context, k cid.Cid) (int, error) { if !k.Defined() { return -1, ipld.ErrNotFound{Cid: k} } @@ -168,7 +171,7 @@ func (b *arccache) GetSize(ctx context.Context, k cid.Cid) (int, error) { return blockSize, err } -func (b *arccache) View(ctx context.Context, k cid.Cid, callback func([]byte) error) error { +func (b *tqcache) View(ctx context.Context, k cid.Cid, callback func([]byte) error) error { // shortcircuit and fall back to Get if the underlying store // doesn't support Viewer. if b.viewer == nil { @@ -212,7 +215,7 @@ func (b *arccache) View(ctx context.Context, k cid.Cid, callback func([]byte) er return cberr } -func (b *arccache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { +func (b *tqcache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { if !k.Defined() { return nil, ipld.ErrNotFound{Cid: k} } @@ -235,7 +238,7 @@ func (b *arccache) Get(ctx context.Context, k cid.Cid) (blocks.Block, error) { return bl, err } -func (b *arccache) Put(ctx context.Context, bl blocks.Block) error { +func (b *tqcache) Put(ctx context.Context, bl blocks.Block) error { key := cacheKey(bl.Cid()) if has, _, ok := b.queryCache(key); ok && has { @@ -310,7 +313,7 @@ func newKeyedBlocks(cap int) *keyedBlocks { } } -func (b *arccache) PutMany(ctx context.Context, bs []blocks.Block) error { +func (b *tqcache) PutMany(ctx context.Context, bs []blocks.Block) error { good := newKeyedBlocks(len(bs)) for _, blk := range bs { // call put on block if result is inconclusive or we are sure that @@ -348,19 +351,19 @@ func (b *arccache) PutMany(ctx context.Context, bs []blocks.Block) error { return nil } -func (b *arccache) HashOnRead(enabled bool) { +func (b *tqcache) HashOnRead(enabled bool) { b.blockstore.HashOnRead(enabled) } -func (b *arccache) cacheHave(key string, have bool) { +func (b *tqcache) cacheHave(key string, have bool) { b.cache.Add(key, cacheHave(have)) } -func (b *arccache) cacheSize(key string, blockSize int) { +func (b *tqcache) cacheSize(key string, blockSize int) { b.cache.Add(key, cacheSize(blockSize)) } -func (b *arccache) cacheInvalidate(key string) { +func (b *tqcache) cacheInvalidate(key string) { b.cache.Remove(key) } @@ -375,7 +378,7 @@ func (b *arccache) cacheInvalidate(key string) { // // When ok is true, exists carries the correct answer, and size carries the // size, if known, or -1 if not. -func (b *arccache) queryCache(k string) (exists bool, size int, ok bool) { +func (b *tqcache) queryCache(k string) (exists bool, size int, ok bool) { b.total.Inc() h, ok := b.cache.Get(k) @@ -391,18 +394,18 @@ func (b *arccache) queryCache(k string) (exists bool, size int, ok bool) { return false, -1, false } -func (b *arccache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { +func (b *tqcache) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) { return b.blockstore.AllKeysChan(ctx) } -func (b *arccache) GCLock(ctx context.Context) Unlocker { +func (b *tqcache) GCLock(ctx context.Context) Unlocker { return b.blockstore.(GCBlockstore).GCLock(ctx) } -func (b *arccache) PinLock(ctx context.Context) Unlocker { +func (b *tqcache) PinLock(ctx context.Context) Unlocker { return b.blockstore.(GCBlockstore).PinLock(ctx) } -func (b *arccache) GCRequested(ctx context.Context) bool { +func (b *tqcache) GCRequested(ctx context.Context) bool { return b.blockstore.(GCBlockstore).GCRequested(ctx) } diff --git a/blockstore/arc_cache_test.go b/blockstore/twoqueue_cache_test.go similarity index 61% rename from blockstore/arc_cache_test.go rename to blockstore/twoqueue_cache_test.go index 164457d1b..b4ed91893 100644 --- a/blockstore/arc_cache_test.go +++ b/blockstore/twoqueue_cache_test.go @@ -17,7 +17,7 @@ import ( var exampleBlock = blocks.NewBlock([]byte("foo")) -func testArcCached(ctx context.Context, bs Blockstore) (*arccache, error) { +func testTwoQueueCached(ctx context.Context, bs Blockstore) (*tqcache, error) { if ctx == nil { ctx = context.TODO() } @@ -26,19 +26,19 @@ func testArcCached(ctx context.Context, bs Blockstore) (*arccache, error) { opts.HasBloomFilterHashes = 0 bbs, err := CachedBlockstore(ctx, bs, opts) if err == nil { - return bbs.(*arccache), nil + return bbs.(*tqcache), nil } return nil, err } -func createStores(t testing.TB) (*arccache, Blockstore, *callbackDatastore) { +func createStores(t testing.TB) (*tqcache, Blockstore, *callbackDatastore) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - arc, err := testArcCached(context.TODO(), bs) + c, err := testTwoQueueCached(context.TODO(), bs) if err != nil { t.Fatal(err) } - return arc, bs, cd + return c, bs, cd } func trap(message string, cd *callbackDatastore, t *testing.T) { @@ -51,9 +51,9 @@ func untrap(cd *callbackDatastore) { } func TestRemoveCacheEntryOnDelete(t *testing.T) { - arc, _, cd := createStores(t) + c, _, cd := createStores(t) - arc.Put(bg, exampleBlock) + c.Put(bg, exampleBlock) cd.Lock() writeHitTheDatastore := false @@ -63,102 +63,102 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { writeHitTheDatastore = true }) - arc.DeleteBlock(bg, exampleBlock.Cid()) - arc.Put(bg, exampleBlock) + c.DeleteBlock(bg, exampleBlock.Cid()) + c.Put(bg, exampleBlock) if !writeHitTheDatastore { t.Fail() } } func TestElideDuplicateWrite(t *testing.T) { - arc, _, cd := createStores(t) + c, _, cd := createStores(t) - arc.Put(bg, exampleBlock) + c.Put(bg, exampleBlock) trap("write hit datastore", cd, t) - arc.Put(bg, exampleBlock) + c.Put(bg, exampleBlock) } func TestHasRequestTriggersCache(t *testing.T) { - arc, _, cd := createStores(t) + c, _, cd := createStores(t) - arc.Has(bg, exampleBlock.Cid()) + c.Has(bg, exampleBlock.Cid()) trap("has hit datastore", cd, t) - if has, err := arc.Has(bg, exampleBlock.Cid()); has || err != nil { + if has, err := c.Has(bg, exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } untrap(cd) - err := arc.Put(bg, exampleBlock) + err := c.Put(bg, exampleBlock) if err != nil { t.Fatal(err) } trap("has hit datastore", cd, t) - if has, err := arc.Has(bg, exampleBlock.Cid()); !has || err != nil { + if has, err := c.Has(bg, exampleBlock.Cid()); !has || err != nil { t.Fatal("has returned invalid result") } } func TestGetFillsCache(t *testing.T) { - arc, _, cd := createStores(t) + c, _, cd := createStores(t) - if bl, err := arc.Get(bg, exampleBlock.Cid()); bl != nil || err == nil { + if bl, err := c.Get(bg, exampleBlock.Cid()); bl != nil || err == nil { t.Fatal("block was found or there was no error") } trap("has hit datastore", cd, t) - if has, err := arc.Has(bg, exampleBlock.Cid()); has || err != nil { + if has, err := c.Has(bg, exampleBlock.Cid()); has || err != nil { t.Fatal("has was true but there is no such block") } - if _, err := arc.GetSize(bg, exampleBlock.Cid()); !ipld.IsNotFound(err) { + if _, err := c.GetSize(bg, exampleBlock.Cid()); !ipld.IsNotFound(err) { t.Fatal("getsize was true but there is no such block") } untrap(cd) - if err := arc.Put(bg, exampleBlock); err != nil { + if err := c.Put(bg, exampleBlock); err != nil { t.Fatal(err) } trap("has hit datastore", cd, t) - if has, err := arc.Has(bg, exampleBlock.Cid()); !has || err != nil { + if has, err := c.Has(bg, exampleBlock.Cid()); !has || err != nil { t.Fatal("has returned invalid result") } - if blockSize, err := arc.GetSize(bg, exampleBlock.Cid()); blockSize == -1 || err != nil { + if blockSize, err := c.GetSize(bg, exampleBlock.Cid()); blockSize == -1 || err != nil { t.Fatal("getsize returned invalid result", blockSize, err) } } func TestGetAndDeleteFalseShortCircuit(t *testing.T) { - arc, _, cd := createStores(t) + c, _, cd := createStores(t) - arc.Has(bg, exampleBlock.Cid()) - arc.GetSize(bg, exampleBlock.Cid()) + c.Has(bg, exampleBlock.Cid()) + c.GetSize(bg, exampleBlock.Cid()) trap("get hit datastore", cd, t) - if bl, err := arc.Get(bg, exampleBlock.Cid()); bl != nil || !ipld.IsNotFound(err) { + if bl, err := c.Get(bg, exampleBlock.Cid()); bl != nil || !ipld.IsNotFound(err) { t.Fatal("get returned invalid result") } - if arc.DeleteBlock(bg, exampleBlock.Cid()) != nil { + if c.DeleteBlock(bg, exampleBlock.Cid()) != nil { t.Fatal("expected deletes to be idempotent") } } -func TestArcCreationFailure(t *testing.T) { - if arc, err := newARCCachedBS(context.TODO(), nil, -1); arc != nil || err == nil { +func TestCacheCreationFailure(t *testing.T) { + if c, err := newTwoQueueCachedBS(context.TODO(), nil, -1); c != nil || err == nil { t.Fatal("expected error and no cache") } } func TestInvalidKey(t *testing.T) { - arc, _, _ := createStores(t) + c, _, _ := createStores(t) - bl, err := arc.Get(bg, cid.Cid{}) + bl, err := c.Get(bg, cid.Cid{}) if bl != nil { t.Fatal("blocks should be nil") @@ -169,32 +169,32 @@ func TestInvalidKey(t *testing.T) { } func TestHasAfterSucessfulGetIsCached(t *testing.T) { - arc, bs, cd := createStores(t) + c, bs, cd := createStores(t) bs.Put(bg, exampleBlock) - arc.Get(bg, exampleBlock.Cid()) + c.Get(bg, exampleBlock.Cid()) trap("has hit datastore", cd, t) - arc.Has(bg, exampleBlock.Cid()) + c.Has(bg, exampleBlock.Cid()) } func TestGetSizeAfterSucessfulGetIsCached(t *testing.T) { - arc, bs, cd := createStores(t) + c, bs, cd := createStores(t) bs.Put(bg, exampleBlock) - arc.Get(bg, exampleBlock.Cid()) + c.Get(bg, exampleBlock.Cid()) trap("has hit datastore", cd, t) - arc.GetSize(bg, exampleBlock.Cid()) + c.GetSize(bg, exampleBlock.Cid()) } func TestGetSizeAfterSucessfulHas(t *testing.T) { - arc, bs, _ := createStores(t) + c, bs, _ := createStores(t) bs.Put(bg, exampleBlock) - has, err := arc.Has(bg, exampleBlock.Cid()) + has, err := c.Has(bg, exampleBlock.Cid()) if err != nil { t.Fatal(err) } @@ -202,7 +202,7 @@ func TestGetSizeAfterSucessfulHas(t *testing.T) { t.Fatal("expected to have block") } - if size, err := arc.GetSize(bg, exampleBlock.Cid()); err != nil { + if size, err := c.GetSize(bg, exampleBlock.Cid()); err != nil { t.Fatal(err) } else if size != len(exampleBlock.RawData()) { t.Fatalf("expected size %d, got %d", len(exampleBlock.RawData()), size) @@ -210,34 +210,34 @@ func TestGetSizeAfterSucessfulHas(t *testing.T) { } func TestGetSizeMissingZeroSizeBlock(t *testing.T) { - arc, bs, cd := createStores(t) + c, bs, cd := createStores(t) emptyBlock := blocks.NewBlock([]byte{}) missingBlock := blocks.NewBlock([]byte("missingBlock")) bs.Put(bg, emptyBlock) - arc.Get(bg, emptyBlock.Cid()) + c.Get(bg, emptyBlock.Cid()) trap("has hit datastore", cd, t) - if blockSize, err := arc.GetSize(bg, emptyBlock.Cid()); blockSize != 0 || err != nil { + if blockSize, err := c.GetSize(bg, emptyBlock.Cid()); blockSize != 0 || err != nil { t.Fatal("getsize returned invalid result") } untrap(cd) - arc.Get(bg, missingBlock.Cid()) + c.Get(bg, missingBlock.Cid()) trap("has hit datastore", cd, t) - if _, err := arc.GetSize(bg, missingBlock.Cid()); !ipld.IsNotFound(err) { + if _, err := c.GetSize(bg, missingBlock.Cid()); !ipld.IsNotFound(err) { t.Fatal("getsize returned invalid result") } } func TestDifferentKeyObjectsWork(t *testing.T) { - arc, bs, cd := createStores(t) + c, bs, cd := createStores(t) bs.Put(bg, exampleBlock) - arc.Get(bg, exampleBlock.Cid()) + c.Get(bg, exampleBlock.Cid()) trap("has hit datastore", cd, t) cidstr := exampleBlock.Cid().String() @@ -247,42 +247,42 @@ func TestDifferentKeyObjectsWork(t *testing.T) { t.Fatal(err) } - arc.Has(bg, ncid) + c.Has(bg, ncid) } func TestPutManyCaches(t *testing.T) { t.Run("happy path PutMany", func(t *testing.T) { - arc, _, cd := createStores(t) - arc.PutMany(bg, []blocks.Block{exampleBlock}) + c, _, cd := createStores(t) + c.PutMany(bg, []blocks.Block{exampleBlock}) trap("has hit datastore", cd, t) - arc.Has(bg, exampleBlock.Cid()) - arc.GetSize(bg, exampleBlock.Cid()) + c.Has(bg, exampleBlock.Cid()) + c.GetSize(bg, exampleBlock.Cid()) untrap(cd) - arc.DeleteBlock(bg, exampleBlock.Cid()) + c.DeleteBlock(bg, exampleBlock.Cid()) - arc.Put(bg, exampleBlock) + c.Put(bg, exampleBlock) trap("PunMany has hit datastore", cd, t) - arc.PutMany(bg, []blocks.Block{exampleBlock}) + c.PutMany(bg, []blocks.Block{exampleBlock}) }) t.Run("PutMany with duplicates", func(t *testing.T) { - arc, _, cd := createStores(t) - arc.PutMany(bg, []blocks.Block{exampleBlock, exampleBlock}) + c, _, cd := createStores(t) + c.PutMany(bg, []blocks.Block{exampleBlock, exampleBlock}) trap("has hit datastore", cd, t) - arc.Has(bg, exampleBlock.Cid()) - arc.GetSize(bg, exampleBlock.Cid()) + c.Has(bg, exampleBlock.Cid()) + c.GetSize(bg, exampleBlock.Cid()) untrap(cd) - arc.DeleteBlock(bg, exampleBlock.Cid()) + c.DeleteBlock(bg, exampleBlock.Cid()) - arc.Put(bg, exampleBlock) + c.Put(bg, exampleBlock) trap("PunMany has hit datastore", cd, t) - arc.PutMany(bg, []blocks.Block{exampleBlock}) + c.PutMany(bg, []blocks.Block{exampleBlock}) }) } -func BenchmarkARCCacheConcurrentOps(b *testing.B) { +func BenchmarkTwoQueueCacheConcurrentOps(b *testing.B) { // ~4k blocks seems high enough to be realistic, // but low enough to cause collisions. // Keep it as a power of 2, to simplify code below. @@ -305,10 +305,10 @@ func BenchmarkARCCacheConcurrentOps(b *testing.B) { // Each test begins with half the blocks present in the cache. // This allows test cases to have both hits and misses, // regardless of whether or not they do Puts. - putHalfBlocks := func(arc *arccache) { + putHalfBlocks := func(c *tqcache) { for i, block := range dummyBlocks { if i%2 == 0 { - if err := arc.Put(bg, block); err != nil { + if err := c.Put(bg, block); err != nil { b.Fatal(err) } } @@ -319,30 +319,30 @@ func BenchmarkARCCacheConcurrentOps(b *testing.B) { const numOps = 2 var testOps = []struct { name string - ops [numOps]func(*arccache, blocks.Block) + ops [numOps]func(*tqcache, blocks.Block) }{ - {"PutDelete", [...]func(*arccache, blocks.Block){ - func(arc *arccache, block blocks.Block) { - arc.Put(bg, block) + {"PutDelete", [...]func(*tqcache, blocks.Block){ + func(c *tqcache, block blocks.Block) { + c.Put(bg, block) }, - func(arc *arccache, block blocks.Block) { - arc.DeleteBlock(bg, block.Cid()) + func(c *tqcache, block blocks.Block) { + c.DeleteBlock(bg, block.Cid()) }, }}, - {"GetDelete", [...]func(*arccache, blocks.Block){ - func(arc *arccache, block blocks.Block) { - arc.Get(bg, block.Cid()) + {"GetDelete", [...]func(*tqcache, blocks.Block){ + func(c *tqcache, block blocks.Block) { + c.Get(bg, block.Cid()) }, - func(arc *arccache, block blocks.Block) { - arc.DeleteBlock(bg, block.Cid()) + func(c *tqcache, block blocks.Block) { + c.DeleteBlock(bg, block.Cid()) }, }}, - {"GetPut", [...]func(*arccache, blocks.Block){ - func(arc *arccache, block blocks.Block) { - arc.Get(bg, block.Cid()) + {"GetPut", [...]func(*tqcache, blocks.Block){ + func(c *tqcache, block blocks.Block) { + c.Get(bg, block.Cid()) }, - func(arc *arccache, block blocks.Block) { - arc.Put(bg, block) + func(c *tqcache, block blocks.Block) { + c.Put(bg, block) }, }}, } @@ -350,8 +350,8 @@ func BenchmarkARCCacheConcurrentOps(b *testing.B) { for _, test := range testOps { test := test // prevent reuse of the range var b.Run(test.name, func(b *testing.B) { - arc, _, _ := createStores(b) - putHalfBlocks(arc) + c, _, _ := createStores(b) + putHalfBlocks(c) var opCounts [numOps]uint64 b.ResetTimer() @@ -366,7 +366,7 @@ func BenchmarkARCCacheConcurrentOps(b *testing.B) { block := dummyBlocks[blockIdx] op := test.ops[opIdx] - op(arc, block) + op(c, block) atomic.AddUint64(&opCounts[opIdx], 1) } diff --git a/examples/go.mod b/examples/go.mod index 90f408492..9d2561479 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -54,6 +54,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect github.com/huin/goupnp v1.0.3 // indirect github.com/ipfs/bbloom v0.0.4 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect diff --git a/examples/go.sum b/examples/go.sum index 5930e46ca..c2fb692b2 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -268,6 +268,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= +github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= diff --git a/go.mod b/go.mod index 476a38977..7d4bb79df 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 - github.com/hashicorp/golang-lru v0.5.4 + github.com/hashicorp/golang-lru/v2 v2.0.1 github.com/ipfs/bbloom v0.0.4 github.com/ipfs/go-bitfield v1.1.0 github.com/ipfs/go-block-format v0.1.2 @@ -106,6 +106,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/huin/goupnp v1.0.3 // indirect github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect github.com/ipfs/go-ipfs-chunker v0.0.5 // indirect diff --git a/go.sum b/go.sum index c3361eb78..8edd50563 100644 --- a/go.sum +++ b/go.sum @@ -272,6 +272,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= +github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= diff --git a/namesys/namesys.go b/namesys/namesys.go index d031064e6..de74f1488 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -19,7 +19,7 @@ import ( "strings" "time" - lru "github.com/hashicorp/golang-lru" + lru "github.com/hashicorp/golang-lru/v2" opts "github.com/ipfs/boxo/coreiface/options/namesys" "github.com/ipfs/boxo/path" "github.com/ipfs/go-cid" @@ -48,7 +48,7 @@ type mpns struct { ipnsPublisher Publisher staticMap map[string]path.Path - cache *lru.Cache + cache *lru.Cache[string, any] } type Option func(*mpns) error @@ -60,7 +60,7 @@ func WithCache(size int) Option { return fmt.Errorf("invalid cache size %d; must be > 0", size) } - cache, err := lru.New(size) + cache, err := lru.New[string, any](size) if err != nil { return err }