Skip to content

Commit c170b12

Browse files
authored
Do not allow batch to be reused after commit (#67)
By explicitly not allowing the Batch to be reused, the Batch resources can be recycled, after Commit is called, for use in a new Batch. In general, a Batch should not be reuesd for this or any other datastore becuase it is not guaranteed to be safely reusable for all datastores. Therefore, making this Batch explicitly not reusable allows better resource management as well as returning a clear error when the Batch is mistakenly reused.
1 parent 869f660 commit c170b12

File tree

2 files changed

+26
-3
lines changed

2 files changed

+26
-3
lines changed

datastore.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package pebbleds
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"sync"
78
"sync/atomic"
@@ -419,7 +420,12 @@ type Batch struct {
419420

420421
var _ ds.Batch = (*Batch)(nil)
421422

423+
var ErrBatchCommitted = errors.New("batch already committed and cannot be reused")
424+
422425
func (b *Batch) Put(ctx context.Context, key ds.Key, value []byte) error {
426+
if b.batch == nil {
427+
return ErrBatchCommitted
428+
}
423429
err := b.batch.Set(key.Bytes(), value, b.writeOptions)
424430
if err != nil {
425431
return fmt.Errorf("pebble error during set within batch: %w", err)
@@ -428,6 +434,9 @@ func (b *Batch) Put(ctx context.Context, key ds.Key, value []byte) error {
428434
}
429435

430436
func (b *Batch) Delete(ctx context.Context, key ds.Key) error {
437+
if b.batch == nil {
438+
return ErrBatchCommitted
439+
}
431440
err := b.batch.Delete(key.Bytes(), b.writeOptions)
432441
if err != nil {
433442
return fmt.Errorf("pebble error during delete within batch: %w", err)
@@ -436,6 +445,14 @@ func (b *Batch) Delete(ctx context.Context, key ds.Key) error {
436445
}
437446

438447
func (b *Batch) Commit(ctx context.Context) error {
439-
defer b.batch.Reset() // make batch reusable
440-
return b.batch.Commit(b.writeOptions)
448+
if b.batch == nil {
449+
return ErrBatchCommitted
450+
}
451+
if err := b.batch.Commit(b.writeOptions); err != nil {
452+
return err
453+
}
454+
// Recycle batch resources, when safe, for use in a new batch.
455+
b.batch.Close()
456+
b.batch = nil
457+
return nil
441458
}

datastore_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"encoding/base32"
7+
"errors"
78
"math/rand/v2"
89
"os"
910
"testing"
@@ -158,8 +159,13 @@ func TestBatch(t *testing.T) {
158159
t.Fatal(err)
159160
}
160161

161-
// second commit should do nothing, but should not cause an error
162+
// second commit should return a ErrBatchCommitted error.
162163
err = batch.Commit(ctx)
164+
if !errors.Is(err, ErrBatchCommitted) {
165+
t.Fatalf("expected error: %s, got %v", ErrBatchCommitted, err)
166+
}
167+
168+
batch, err = ds.Batch(ctx)
163169
if err != nil {
164170
t.Fatal(err)
165171
}

0 commit comments

Comments
 (0)