mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
handle truncated/damaged .available.shards
The available shards file is just a hint to save us a bit of time later; we don't need it to run and it can get updated pretty easily later. If we have problems reading it, we should just report the error, nuke the file, and continue without it.
This commit is contained in:
parent
1262cd18e0
commit
538768ea9d
2 changed files with 75 additions and 6 deletions
24
field.go
24
field.go
|
|
@ -316,18 +316,30 @@ func (f *Field) mergeRemoteAvailableShards(b *roaring.Bitmap) {
|
|||
|
||||
// loadAvailableShards reads remoteAvailableShards data for the field, if any.
|
||||
func (f *Field) loadAvailableShards() error {
|
||||
bm := roaring.NewBitmap()
|
||||
// Read data from meta file.
|
||||
path := filepath.Join(f.path, ".available.shards")
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
// doesn't exist: this is fine
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err, "reading available shards")
|
||||
} else {
|
||||
if err := bm.UnmarshalBinary(buf); err != nil {
|
||||
return errors.Wrap(err, "unmarshaling")
|
||||
}
|
||||
// some other problem:
|
||||
if err != nil {
|
||||
f.logger.Printf("available shards file present but unreadable, discarding: %v", err)
|
||||
err = os.Remove(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "deleting corrupt available shards list")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
bm := roaring.NewBitmap()
|
||||
if err = bm.UnmarshalBinary(buf); err != nil {
|
||||
f.logger.Printf("available shards file corrupt, discarding: %v", err)
|
||||
err = os.Remove(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "deleting corrupt available shards list")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Merge bitmap from file into field.
|
||||
f.mergeRemoteAvailableShards(bm)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -366,6 +367,62 @@ func TestField_PersistAvailableShards(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestField_CorruptAvailableShards(t *testing.T) {
|
||||
f := MustOpenField(OptFieldTypeDefault())
|
||||
|
||||
// bm represents remote available shards.
|
||||
bm := roaring.NewBitmap(1, 2, 3)
|
||||
|
||||
if err := f.AddRemoteAvailableShards(bm); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
path := filepath.Join(f.path, ".available.shards")
|
||||
|
||||
avail, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n, err := avail.Write([]byte{23})
|
||||
if err != nil || n != 1 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
avail.Close()
|
||||
|
||||
// Reload field and verify that shard data is persisted.
|
||||
if err := f.Reopen(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(f.remoteAvailableShards.Slice(), []uint64(nil)) {
|
||||
t.Fatalf("unexpected available shards (reopen). expected: %#v, but got: %#v", []uint64{}, f.remoteAvailableShards.Slice())
|
||||
}
|
||||
}
|
||||
|
||||
func TestField_TruncatedAvailableShards(t *testing.T) {
|
||||
f := MustOpenField(OptFieldTypeDefault())
|
||||
|
||||
// bm represents remote available shards.
|
||||
bm := roaring.NewBitmap(1, 2, 3)
|
||||
|
||||
if err := f.AddRemoteAvailableShards(bm); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
path := filepath.Join(f.path, ".available.shards")
|
||||
|
||||
avail, err := os.OpenFile(path, os.O_TRUNC|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
avail.Close()
|
||||
|
||||
// Reload field and verify that shard data is persisted.
|
||||
if err := f.Reopen(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(f.remoteAvailableShards.Slice(), []uint64(nil)) {
|
||||
t.Fatalf("unexpected available shards (reopen). expected: %#v, but got: %#v", []uint64{}, f.remoteAvailableShards.Slice())
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that persisting available shards having a smaller footprint (for example,
|
||||
// when going from a bitmap to a smaller, RLE representation) succeeds.
|
||||
func TestField_PersistAvailableShardsFootprint(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue