mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
```bash for file in `cat diffys`; do printf '%s\n%s\n' "// Copyright 2021 Molecula Corp. All rights reserved." "$(cat $file)" >$file; done ```
201 lines
6 KiB
Go
201 lines
6 KiB
Go
// Copyright 2021 Molecula Corp. All rights reserved.
|
|
package pilosa
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"io"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/molecula/featurebase/v2/testhook"
|
|
bolt "go.etcd.io/bbolt"
|
|
)
|
|
|
|
func TestIDAlloc(t *testing.T) {
|
|
// Acquire a temporary file.
|
|
f, err := testhook.TempFile(t, "idalloc")
|
|
if err != nil {
|
|
t.Errorf("acquiring temporary file: %v", err)
|
|
return
|
|
}
|
|
defer func() {
|
|
cerr := f.Close()
|
|
if cerr != nil {
|
|
t.Errorf("closing temporary file: %v", cerr)
|
|
}
|
|
}()
|
|
|
|
// Open bolt.
|
|
db, err := bolt.Open(f.Name(), 0666, &bolt.Options{Timeout: 1 * time.Second, NoSync: true})
|
|
if err != nil {
|
|
t.Errorf("opening bolt: %v", err)
|
|
return
|
|
}
|
|
defer func() {
|
|
cerr := db.Close()
|
|
if cerr != nil {
|
|
t.Errorf("closing bolt: %v", cerr)
|
|
}
|
|
}()
|
|
|
|
alloc := idAllocator{db: db}
|
|
|
|
a := IDAllocKey{
|
|
Index: "h",
|
|
Key: "a",
|
|
}
|
|
b := IDAllocKey{
|
|
Index: "h",
|
|
Key: "b",
|
|
}
|
|
var sessions [11][32]byte
|
|
for i := range sessions {
|
|
_, err = io.ReadFull(rand.Reader, sessions[i][:])
|
|
if err != nil {
|
|
t.Errorf("failed to obtain entropy: %v", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Reserve 2 batches of IDs and then commit both.
|
|
if ranges, err := alloc.reserve(a, sessions[0], ^uint64(0), 3); err != nil {
|
|
t.Errorf("failed to reserve 3 IDs: %v", err)
|
|
} else {
|
|
expect := []IDRange{{1, 3}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
if ranges, err := alloc.reserve(b, sessions[1], ^uint64(0), 3); err != nil {
|
|
t.Errorf("failed to reserve 3 IDs: %v", err)
|
|
} else {
|
|
expect := []IDRange{{4, 6}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
if err := alloc.commit(a, sessions[0], 3); err != nil {
|
|
t.Errorf("failed to commit reservation of 3 IDs: %v", err)
|
|
}
|
|
if err := alloc.commit(b, sessions[1], 3); err != nil {
|
|
t.Errorf("failed to commit reservation of 3 IDs: %v", err)
|
|
}
|
|
|
|
// Test collision handling.
|
|
if ranges, err := alloc.reserve(a, sessions[2], ^uint64(0), 4); err != nil {
|
|
t.Errorf("failed to reserve 4 IDs: %v", err)
|
|
} else {
|
|
expect := []IDRange{{7, 10}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
if ranges, err := alloc.reserve(a, sessions[3], ^uint64(0), 4); err != nil {
|
|
t.Errorf("failed to reserve 4 IDs: %v", err)
|
|
} else {
|
|
// Instead of reserving new ranges, we have taken over the previous reservation.
|
|
expect := []IDRange{{7, 10}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
if err := alloc.commit(a, sessions[2], 4); err == nil {
|
|
// This was taken over by reservation 3, so this should have warned us that something went wrong.
|
|
t.Error("unexpected commit success for clobbered reservation")
|
|
}
|
|
if err := alloc.commit(a, sessions[3], 4); err != nil {
|
|
t.Errorf("failed to commit reservation of 4 IDs: %v", err)
|
|
}
|
|
|
|
// Test key return.
|
|
if ranges, err := alloc.reserve(a, sessions[4], ^uint64(0), 100); err != nil {
|
|
t.Errorf("failed to reserve 100 IDs: %v", err)
|
|
} else {
|
|
// Instead of reserving new ranges, we have taken over the previous reservation.
|
|
expect := []IDRange{{11, 110}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
if err := alloc.commit(a, sessions[4], 1); err != nil {
|
|
t.Errorf("failed to commit reservation of 1 ID: %v", err)
|
|
}
|
|
if ranges, err := alloc.reserve(a, sessions[5], ^uint64(0), 100); err != nil {
|
|
t.Errorf("failed to reserve 100 IDs: %v", err)
|
|
} else {
|
|
// Instead of reserving new ranges, we have taken over the previous reservation.
|
|
expect := []IDRange{{12, 111}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
if err := alloc.commit(a, sessions[5], 100); err != nil {
|
|
t.Errorf("failed to commit reservation of 100 IDs: %v", err)
|
|
}
|
|
|
|
// Test offset-based allocation.
|
|
p1 := IDAllocKey{
|
|
Index: "xyzzy",
|
|
Key: "kafka-partition-1",
|
|
}
|
|
p2 := IDAllocKey{
|
|
Index: "xyzzy",
|
|
Key: "kafka-partition-2",
|
|
}
|
|
// Start processsing on 2 partitions.
|
|
if ranges, err := alloc.reserve(p1, sessions[6], 0, 3); err != nil {
|
|
t.Errorf("failed to reserve 3 IDs: %v", err)
|
|
} else {
|
|
expect := []IDRange{{1, 3}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
if ranges, err := alloc.reserve(p2, sessions[7], 0, 3); err != nil {
|
|
t.Errorf("failed to reserve 3 IDs: %v", err)
|
|
} else {
|
|
expect := []IDRange{{4, 6}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
// Partition 2 finishes a batch of 2 IDs.
|
|
if err := alloc.commit(p2, sessions[7], 2); err != nil {
|
|
t.Errorf("failed to commit reservation of 2 IDs: %v", err)
|
|
}
|
|
// Partition 2 reserves more IDs.
|
|
if ranges, err := alloc.reserve(p2, sessions[8], 2, 3); err != nil {
|
|
t.Errorf("failed to reserve 3 IDs: %v", err)
|
|
} else {
|
|
// This could be coalesced here, but that is not true in the general case.
|
|
expect := []IDRange{{6, 6}, {7, 8}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
// Partition 1 ingester failed and was restarted.
|
|
if ranges, err := alloc.reserve(p1, sessions[9], 0, 3); err != nil {
|
|
t.Errorf("failed to reserve 3 IDs: %v", err)
|
|
} else {
|
|
expect := []IDRange{{1, 3}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
// This time, the partition 1 ingester finishes the batch.
|
|
// It commits the offset to Kafka but fails to commit to the ID allocator.
|
|
// It is restarted at Kafka's next offset.
|
|
if ranges, err := alloc.reserve(p1, sessions[10], 3, 4); err != nil {
|
|
t.Errorf("failed to reserve 3 IDs: %v", err)
|
|
} else {
|
|
expect := []IDRange{{9, 12}}
|
|
if !reflect.DeepEqual(expect, ranges) {
|
|
t.Errorf("expeced %v but got %v", expect, ranges)
|
|
}
|
|
}
|
|
// Partition 1 finishes a batch of 4 IDs.
|
|
if err := alloc.commit(p1, sessions[10], 4); err != nil {
|
|
t.Errorf("failed to commit reservation of 4 IDs: %v", err)
|
|
}
|
|
}
|