Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blockstore: use errors when API contracts are broken #195

Merged
merged 1 commit into from
Aug 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions v2/blockstore/readonly.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import (

var _ blockstore.Blockstore = (*ReadOnly)(nil)

var errZeroLengthSection = fmt.Errorf("zero-length section not allowed by default; see WithZeroLengthSectionAsEOF option")
var (
errZeroLengthSection = fmt.Errorf("zero-length carv2 section not allowed by default; see WithZeroLengthSectionAsEOF option")
errReadOnly = fmt.Errorf("called write method on a read-only carv2 blockstore")
)

type (
// ReadOnly provides a read-only CAR Block Store.
Expand Down Expand Up @@ -176,7 +179,7 @@ func (b *ReadOnly) readBlock(idx int64) (cid.Cid, []byte, error) {

// DeleteBlock is unsupported and always panics.
func (b *ReadOnly) DeleteBlock(_ cid.Cid) error {
panic("called write method on a read-only blockstore")
return errReadOnly
}

// Has indicates if the store contains a block that corresponds to the given key.
Expand Down Expand Up @@ -303,12 +306,12 @@ func (b *ReadOnly) GetSize(key cid.Cid) (int, error) {

// Put is not supported and always returns an error.
func (b *ReadOnly) Put(blocks.Block) error {
panic("called write method on a read-only blockstore")
return errReadOnly
}

// PutMany is not supported and always returns an error.
func (b *ReadOnly) PutMany([]blocks.Block) error {
panic("called write method on a read-only blockstore")
return errReadOnly
}

// WithAsyncErrorHandler returns a context with async error handling set to the given errHandler.
Expand Down
6 changes: 3 additions & 3 deletions v2/blockstore/readonly_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ func TestReadOnly(t *testing.T) {
require.Equal(t, wantBlock, gotBlock)

// Assert write operations panic
require.Panics(t, func() { subject.Put(wantBlock) })
require.Panics(t, func() { subject.PutMany([]blocks.Block{wantBlock}) })
require.Panics(t, func() { subject.DeleteBlock(key) })
require.Error(t, subject.Put(wantBlock))
require.Error(t, subject.PutMany([]blocks.Block{wantBlock}))
require.Error(t, subject.DeleteBlock(key))
}

ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
Expand Down
36 changes: 20 additions & 16 deletions v2/blockstore/readwrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (

var _ blockstore.Blockstore = (*ReadWrite)(nil)

var errFinalized = fmt.Errorf("cannot use a read-write carv2 blockstore after finalizing")

// ReadWrite implements a blockstore that stores blocks in CARv2 format.
// Blocks put into the blockstore can be read back once they are successfully written.
// This implementation is preferable for a write-heavy workload.
Expand Down Expand Up @@ -284,22 +286,22 @@ func (b *ReadWrite) unfinalize() error {
return err
}

func (b *ReadWrite) panicIfFinalized() {
if b.header.DataSize != 0 {
panic("must not use a read-write blockstore after finalizing")
}
func (b *ReadWrite) finalized() bool {
return b.header.DataSize != 0
dirkmc marked this conversation as resolved.
Show resolved Hide resolved
}

// Put puts a given block to the underlying datastore
func (b *ReadWrite) Put(blk blocks.Block) error {
// PutMany already calls panicIfFinalized.
// PutMany already checks b.finalized.
return b.PutMany([]blocks.Block{blk})
}

// PutMany puts a slice of blocks at the same time using batching
// capabilities of the underlying datastore whenever possible.
func (b *ReadWrite) PutMany(blks []blocks.Block) error {
b.panicIfFinalized()
if b.finalized() {
return errFinalized
}

b.mu.Lock()
defer b.mu.Unlock()
Expand Down Expand Up @@ -362,31 +364,33 @@ func (b *ReadWrite) Finalize() error {
}

func (b *ReadWrite) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) {
b.panicIfFinalized()
if b.finalized() {
return nil, errFinalized
}

return b.ReadOnly.AllKeysChan(ctx)
}

func (b *ReadWrite) Has(key cid.Cid) (bool, error) {
b.panicIfFinalized()
if b.finalized() {
return false, errFinalized
}

return b.ReadOnly.Has(key)
}

func (b *ReadWrite) Get(key cid.Cid) (blocks.Block, error) {
b.panicIfFinalized()
if b.finalized() {
return nil, errFinalized
}

return b.ReadOnly.Get(key)
}

func (b *ReadWrite) GetSize(key cid.Cid) (int, error) {
b.panicIfFinalized()
if b.finalized() {
return 0, errFinalized
}

return b.ReadOnly.GetSize(key)
}

func (b *ReadWrite) HashOnRead(h bool) {
b.panicIfFinalized()

b.ReadOnly.HashOnRead(h)
}
26 changes: 16 additions & 10 deletions v2/blockstore/readwrite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,18 +494,24 @@ func TestReadWritePanicsOnlyWhenFinalized(t *testing.T) {
require.True(t, has)

subject.HashOnRead(true)
// Delete should always panic regardless of finalize
require.Panics(t, func() { subject.DeleteBlock(oneTestBlockCid) })
// Delete should always error regardless of finalize
require.Error(t, subject.DeleteBlock(oneTestBlockCid))

require.NoError(t, subject.Finalize())
require.Panics(t, func() { subject.Get(oneTestBlockCid) })
require.Panics(t, func() { subject.GetSize(anotherTestBlockCid) })
require.Panics(t, func() { subject.Has(anotherTestBlockCid) })
require.Panics(t, func() { subject.HashOnRead(true) })
require.Panics(t, func() { subject.Put(oneTestBlockWithCidV1) })
require.Panics(t, func() { subject.PutMany([]blocks.Block{anotherTestBlockWithCidV0}) })
require.Panics(t, func() { subject.AllKeysChan(context.Background()) })
require.Panics(t, func() { subject.DeleteBlock(oneTestBlockCid) })
require.Error(t, subject.Finalize())

_, err = subject.Get(oneTestBlockCid)
require.Error(t, err)
_, err = subject.GetSize(anotherTestBlockCid)
require.Error(t, err)
_, err = subject.Has(anotherTestBlockCid)
require.Error(t, err)

require.Error(t, subject.Put(oneTestBlockWithCidV1))
require.Error(t, subject.PutMany([]blocks.Block{anotherTestBlockWithCidV0}))
_, err = subject.AllKeysChan(context.Background())
require.Error(t, err)
require.Error(t, subject.DeleteBlock(oneTestBlockCid))
}

func TestReadWriteWithPaddingWorksAsExpected(t *testing.T) {
Expand Down