diff --git a/blockstore/autobatch.go b/blockstore/autobatch.go index 90f4ad071..d41d521ef 100644 --- a/blockstore/autobatch.go +++ b/blockstore/autobatch.go @@ -181,18 +181,22 @@ func (bs *AutobatchBlockstore) Get(ctx context.Context, c cid.Cid) (block.Block, } bs.stateLock.Lock() - defer bs.stateLock.Unlock() v, ok := bs.flushingBatch.blockMap[c] if ok { + bs.stateLock.Unlock() return v, nil } v, ok = bs.bufferedBatch.blockMap[c] if ok { + bs.stateLock.Unlock() return v, nil } + bs.stateLock.Unlock() - return bs.Get(ctx, c) + // We have to check the backing store one more time because it may have been flushed by the + // time we were able to take the lock above. + return bs.backingBs.Get(ctx, c) } func (bs *AutobatchBlockstore) DeleteBlock(context.Context, cid.Cid) error { diff --git a/blockstore/autobatch_test.go b/blockstore/autobatch_test.go index 57a3b7d6c..495c6c4db 100644 --- a/blockstore/autobatch_test.go +++ b/blockstore/autobatch_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + ipld "github.com/ipfs/go-ipld-format" "github.com/stretchr/testify/require" ) @@ -29,6 +30,10 @@ func TestAutobatchBlockstore(t *testing.T) { require.NoError(t, err) require.Equal(t, b2.RawData(), v2.RawData()) + // Regression test for a deadlock. + _, err = ab.Get(ctx, b3.Cid()) + require.True(t, ipld.IsNotFound(err)) + require.NoError(t, ab.Flush(ctx)) require.NoError(t, ab.Shutdown(ctx)) } diff --git a/blockstore/union_test.go b/blockstore/union_test.go index a3ca117b2..579489947 100644 --- a/blockstore/union_test.go +++ b/blockstore/union_test.go @@ -13,6 +13,7 @@ var ( b0 = blocks.NewBlock([]byte("abc")) b1 = blocks.NewBlock([]byte("foo")) b2 = blocks.NewBlock([]byte("bar")) + b3 = blocks.NewBlock([]byte("baz")) ) func TestUnionBlockstore_Get(t *testing.T) {