mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
111 lines
3.1 KiB
Go
111 lines
3.1 KiB
Go
package pilosa_test
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"reflect"
|
|
"testing"
|
|
"testing/quick"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/pilosa/pilosa"
|
|
)
|
|
|
|
// Ensure the cluster can fairly distribute partitions across the nodes.
|
|
func TestCluster_Owners(t *testing.T) {
|
|
c := pilosa.Cluster{
|
|
Nodes: []*pilosa.Node{
|
|
{Host: "serverA:1000"},
|
|
{Host: "serverB:1000"},
|
|
{Host: "serverC:1000"},
|
|
},
|
|
Hasher: NewModHasher(),
|
|
ReplicaN: 2,
|
|
}
|
|
|
|
// Verify nodes are distributed.
|
|
if a := c.PartitionNodes(0); !reflect.DeepEqual(a, []*pilosa.Node{c.Nodes[0], c.Nodes[1]}) {
|
|
t.Fatalf("unexpected owners: %s", spew.Sdump(a))
|
|
}
|
|
|
|
// Verify nodes go around the ring.
|
|
if a := c.PartitionNodes(2); !reflect.DeepEqual(a, []*pilosa.Node{c.Nodes[2], c.Nodes[0]}) {
|
|
t.Fatalf("unexpected owners: %s", spew.Sdump(a))
|
|
}
|
|
}
|
|
|
|
// Ensure the partitioner can assign a fragment to a partition.
|
|
func TestCluster_Partition(t *testing.T) {
|
|
if err := quick.Check(func(db string, slice uint64, partitionN int) bool {
|
|
c := pilosa.NewCluster()
|
|
c.PartitionN = partitionN
|
|
|
|
partitionID := c.Partition(db, slice)
|
|
if partitionID < 0 || partitionID >= partitionN {
|
|
t.Errorf("partition out of range: slice=%d, p=%d, n=%d", slice, partitionID, partitionN)
|
|
}
|
|
|
|
return true
|
|
}, &quick.Config{
|
|
Values: func(values []reflect.Value, rand *rand.Rand) {
|
|
values[0], _ = quick.Value(reflect.TypeOf(""), rand)
|
|
values[1] = reflect.ValueOf(uint64(rand.Uint32()))
|
|
values[2] = reflect.ValueOf(rand.Intn(1000) + 1)
|
|
},
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Ensure the hasher can hash correctly.
|
|
func TestHasher(t *testing.T) {
|
|
for _, tt := range []struct {
|
|
key uint64
|
|
bucket []int
|
|
}{
|
|
// Generated from the reference C++ code
|
|
{0, []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
{1, []int{0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 17, 17}},
|
|
{0xdeadbeef, []int{0, 1, 2, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 16, 16, 16}},
|
|
{0x0ddc0ffeebadf00d, []int{0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15}},
|
|
} {
|
|
for i, v := range tt.bucket {
|
|
if got := pilosa.NewHasher().Hash(tt.key, i+1); got != v {
|
|
t.Errorf("hash(%v,%v)=%v, want %v", tt.key, i+1, got, v)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// NewCluster returns a cluster with n nodes and uses a mod-based hasher.
|
|
func NewCluster(n int) *pilosa.Cluster {
|
|
c := pilosa.NewCluster()
|
|
c.ReplicaN = 1
|
|
c.Hasher = NewModHasher()
|
|
|
|
for i := 0; i < n; i++ {
|
|
c.Nodes = append(c.Nodes, &pilosa.Node{
|
|
Host: fmt.Sprintf("host%d", i),
|
|
})
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
// ModHasher represents a simple, mod-based hashing.
|
|
type ModHasher struct{}
|
|
|
|
// NewModHasher returns a new instance of ModHasher with n buckets.
|
|
func NewModHasher() *ModHasher { return &ModHasher{} }
|
|
|
|
func (*ModHasher) Hash(key uint64, n int) int { return int(key) % n }
|
|
|
|
// ConstHasher represents hash that always returns the same index.
|
|
type ConstHasher struct {
|
|
i int
|
|
}
|
|
|
|
// NewConstHasher returns a new instance of ConstHasher that always returns i.
|
|
func NewConstHasher(i int) *ConstHasher { return &ConstHasher{i: i} }
|
|
|
|
func (h *ConstHasher) Hash(key uint64, n int) int { return h.i }
|