mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
Merge branch 'develop' into deadcode
This commit is contained in:
commit
935aaa96f6
12 changed files with 557 additions and 749 deletions
4
Makefile
4
Makefile
|
|
@ -48,8 +48,8 @@ build: vendor
|
|||
# Create a single release build under the build directory
|
||||
release-build: vendor
|
||||
$(MAKE) $(if $(DOCKER_BUILD),docker-)build FLAGS="-o build/pilosa-$(VERSION_ID)/pilosa" RELEASE=1
|
||||
cp NOTICE LICENSE README.md build/pilosa-$(VERSION_ID)
|
||||
$(if $(ENTERPRISE_ENABLED),cp enterprise/COPYING build/pilosa-$(VERSION_ID))
|
||||
cp NOTICE README.md build/pilosa-$(VERSION_ID)
|
||||
$(if $(ENTERPRISE_ENABLED),cp enterprise/COPYING build/pilosa-$(VERSION_ID),cp LICENSE build/pilosa-$(VERSION_ID))
|
||||
tar -cvz -C build -f build/pilosa-$(VERSION_ID).tar.gz pilosa-$(VERSION_ID)/
|
||||
@echo Created release build: build/pilosa-$(VERSION_ID).tar.gz
|
||||
|
||||
|
|
|
|||
945
executor_test.go
945
executor_test.go
File diff suppressed because it is too large
Load diff
1
field.go
1
field.go
|
|
@ -583,7 +583,6 @@ func (f *Field) RecalculateCaches() {
|
|||
// CreateViewIfNotExists returns the named view, creating it if necessary.
|
||||
// Additionally, a CreateViewMessage is sent to the cluster.
|
||||
func (f *Field) CreateViewIfNotExists(name string) (*View, error) {
|
||||
|
||||
view, created, err := f.createViewIfNotExistsBase(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -1752,7 +1752,7 @@ func (s *FragmentSyncer) syncFragment() error {
|
|||
}
|
||||
|
||||
// Retrieve remote blocks.
|
||||
blocks, err := s.Cluster.InternalClient.FragmentBlocks(context.Background(), nil, s.Fragment.index, s.Fragment.field, s.Fragment.shard)
|
||||
blocks, err := s.Cluster.InternalClient.FragmentBlocks(context.Background(), &node.URI, s.Fragment.index, s.Fragment.field, s.Fragment.shard)
|
||||
if err != nil && err != ErrFragmentNotFound {
|
||||
return errors.Wrap(err, "getting blocks")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -564,6 +564,8 @@ func (h *Holder) logStartup() error {
|
|||
// HolderSyncer is an active anti-entropy tool that compares the local holder
|
||||
// with a remote holder based on block checksums and resolves differences.
|
||||
type HolderSyncer struct {
|
||||
mu sync.Mutex
|
||||
|
||||
Holder *Holder
|
||||
|
||||
Node *Node
|
||||
|
|
@ -588,6 +590,8 @@ func (s *HolderSyncer) IsClosing() bool {
|
|||
|
||||
// SyncHolder compares the holder on host with the local holder and resolves differences.
|
||||
func (s *HolderSyncer) SyncHolder() error {
|
||||
s.mu.Lock() // only allow one instance of SyncHolder to be running at a time
|
||||
defer s.mu.Unlock()
|
||||
ti := time.Now()
|
||||
// Iterate over schema in sorted order.
|
||||
for _, di := range s.Holder.Schema() {
|
||||
|
|
|
|||
118
holder_test.go
118
holder_test.go
|
|
@ -24,8 +24,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/pilosa/pilosa"
|
||||
"github.com/pilosa/pilosa/http"
|
||||
"github.com/pilosa/pilosa/pql"
|
||||
"github.com/pilosa/pilosa/test"
|
||||
)
|
||||
|
||||
|
|
@ -350,49 +348,40 @@ func TestHolder_DeleteIndex(t *testing.T) {
|
|||
|
||||
// Ensure holder can sync with a remote holder.
|
||||
func TestHolderSyncer_SyncHolder(t *testing.T) {
|
||||
t.Skip() // Until test.NewServer() works
|
||||
|
||||
s := test.NewServer()
|
||||
defer s.Close()
|
||||
|
||||
uri, err := pilosa.NewURIFromAddress(s.URL)
|
||||
c := test.MustNewCluster(t, 2)
|
||||
c[0].Config.Cluster.ReplicaN = 2
|
||||
c[0].Config.AntiEntropy.Interval = 0
|
||||
c[1].Config.Cluster.ReplicaN = 2
|
||||
c[1].Config.AntiEntropy.Interval = 0
|
||||
err := c.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("starting cluster: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
_, err = c[0].API.CreateIndex(context.Background(), "i", pilosa.IndexOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("creating index i: %v", err)
|
||||
}
|
||||
_, err = c[0].API.CreateIndex(context.Background(), "y", pilosa.IndexOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("creating index y: %v", err)
|
||||
}
|
||||
_, err = c[0].API.CreateField(context.Background(), "i", "f", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, pilosa.DefaultCacheSize))
|
||||
if err != nil {
|
||||
t.Fatalf("creating field f: %v", err)
|
||||
}
|
||||
_, err = c[0].API.CreateField(context.Background(), "i", "f0", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, pilosa.DefaultCacheSize))
|
||||
if err != nil {
|
||||
t.Fatalf("creating field f0: %v", err)
|
||||
}
|
||||
_, err = c[0].API.CreateField(context.Background(), "y", "z", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, pilosa.DefaultCacheSize))
|
||||
if err != nil {
|
||||
t.Fatalf("creating field z in y: %v", err)
|
||||
}
|
||||
|
||||
cluster := test.NewCluster(2)
|
||||
client := http.GetHTTPClient(nil)
|
||||
httpClient := http.NewInternalClientFromURI(uri, client)
|
||||
cluster.InternalClient = httpClient
|
||||
|
||||
// Create a local holder.
|
||||
hldr0 := test.MustOpenHolder()
|
||||
defer hldr0.Close()
|
||||
|
||||
// Create a remote holder wrapped by an HTTP
|
||||
hldr1 := test.MustOpenHolder()
|
||||
defer hldr1.Close()
|
||||
s.Handler.API.Holder = hldr1.Holder
|
||||
s.Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, shards []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
|
||||
e := pilosa.NewExecutor(pilosa.OptExecutorInternalQueryClient(httpClient))
|
||||
e.Holder = hldr1.Holder
|
||||
e.Node = cluster.Nodes[1]
|
||||
e.Cluster = cluster
|
||||
return e.Execute(ctx, index, query, shards, opt)
|
||||
}
|
||||
|
||||
// Mock 2-node, fully replicated cluster.
|
||||
cluster.ReplicaN = 2
|
||||
|
||||
cluster.Nodes[0].URI = pilosa.NewTestURIFromHostPort("localhost", 0)
|
||||
cluster.Nodes[1].URI = *uri
|
||||
|
||||
// Create fields on nodes.
|
||||
for _, hldr := range []*test.Holder{hldr0, hldr1} {
|
||||
hldr.MustCreateFieldIfNotExists("i", "f")
|
||||
hldr.MustCreateFieldIfNotExists("i", "f0")
|
||||
hldr.MustCreateFieldIfNotExists("y", "z")
|
||||
}
|
||||
hldr0 := &test.Holder{Holder: c[0].Server.Holder()}
|
||||
hldr1 := &test.Holder{Holder: c[1].Server.Holder()}
|
||||
|
||||
// Set data on the local holder.
|
||||
hldr0.SetBit("i", "f", 0, 10)
|
||||
|
|
@ -414,42 +403,39 @@ func TestHolderSyncer_SyncHolder(t *testing.T) {
|
|||
hldr1.SetBit("y", "z", 10, (3*ShardWidth)+5)
|
||||
hldr1.SetBit("y", "z", 10, (3*ShardWidth)+7)
|
||||
|
||||
// Set highest shard.
|
||||
hldr0.Index("i").SetRemoteMaxShard(1)
|
||||
hldr0.Index("y").SetRemoteMaxShard(3)
|
||||
|
||||
// Set up syncer.
|
||||
syncer := pilosa.HolderSyncer{
|
||||
Holder: hldr0.Holder,
|
||||
Node: cluster.Nodes[0],
|
||||
Cluster: cluster,
|
||||
Stats: pilosa.NopStatsClient,
|
||||
err = c[0].Server.SyncData()
|
||||
if err != nil {
|
||||
t.Fatalf("syncing node 0: %v", err)
|
||||
}
|
||||
|
||||
if err := syncer.SyncHolder(); err != nil {
|
||||
t.Fatal(err)
|
||||
err = c[1].Server.SyncData()
|
||||
if err != nil {
|
||||
t.Fatalf("syncing node 1: %v", err)
|
||||
}
|
||||
|
||||
// Verify data is the same on both nodes.
|
||||
for i, hldr := range []*test.Holder{hldr0, hldr1} {
|
||||
if a := hldr.Row("i", "f", 0).Columns(); !reflect.DeepEqual(a, []uint64{10, 4000}) {
|
||||
t.Fatalf("unexpected columns(%d/0): %+v", i, a)
|
||||
} else if a := hldr.Row("i", "f", 2).Columns(); !reflect.DeepEqual(a, []uint64{20}) {
|
||||
t.Fatalf("unexpected columns(%d/2): %+v", i, a)
|
||||
} else if a := hldr.Row("i", "f", 3).Columns(); !reflect.DeepEqual(a, []uint64{10}) {
|
||||
t.Fatalf("unexpected columns(%d/3): %+v", i, a)
|
||||
} else if a := hldr.Row("i", "f", 120).Columns(); !reflect.DeepEqual(a, []uint64{10}) {
|
||||
t.Fatalf("unexpected columns(%d/120): %+v", i, a)
|
||||
} else if a := hldr.Row("i", "f", 200).Columns(); !reflect.DeepEqual(a, []uint64{4}) {
|
||||
t.Fatalf("unexpected columns(%d/200): %+v", i, a)
|
||||
t.Errorf("unexpected columns(%d/0): %+v", i, a)
|
||||
}
|
||||
if a := hldr.Row("i", "f", 2).Columns(); !reflect.DeepEqual(a, []uint64{20}) {
|
||||
t.Errorf("unexpected columns(%d/2): %+v", i, a)
|
||||
}
|
||||
if a := hldr.Row("i", "f", 3).Columns(); !reflect.DeepEqual(a, []uint64{10}) {
|
||||
t.Errorf("unexpected columns(%d/3): %+v", i, a)
|
||||
}
|
||||
if a := hldr.Row("i", "f", 120).Columns(); !reflect.DeepEqual(a, []uint64{10}) {
|
||||
t.Errorf("unexpected columns(%d/120): %+v", i, a)
|
||||
}
|
||||
if a := hldr.Row("i", "f", 200).Columns(); !reflect.DeepEqual(a, []uint64{4}) {
|
||||
t.Errorf("unexpected columns(%d/200): %+v", i, a)
|
||||
}
|
||||
|
||||
if a := hldr.Row("i", "f0", 9).Columns(); !reflect.DeepEqual(a, []uint64{ShardWidth + 5}) {
|
||||
t.Fatalf("unexpected columns(%d/d/f0): %+v", i, a)
|
||||
t.Errorf("unexpected columns(%d/d/f0): %+v", i, a)
|
||||
}
|
||||
|
||||
if a := hldr.Row("y", "z", 10).Columns(); !reflect.DeepEqual(a, []uint64{(3 * ShardWidth) + 4, (3 * ShardWidth) + 5, (3 * ShardWidth) + 7}) {
|
||||
t.Fatalf("unexpected columns(%d/y/z): %+v", i, a)
|
||||
t.Errorf("unexpected columns(%d/y/z): %+v", i, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -717,6 +717,9 @@ func (c *InternalClient) FragmentBlocks(ctx context.Context, uri *pilosa.URI, in
|
|||
|
||||
// BlockData returns row/column id pairs for a block.
|
||||
func (c *InternalClient) BlockData(ctx context.Context, uri *pilosa.URI, index, field string, shard uint64, block int) ([]uint64, []uint64, error) {
|
||||
if uri == nil {
|
||||
panic("need to pass a URI to BlockData")
|
||||
}
|
||||
buf, err := proto.Marshal(&internal.BlockDataRequest{
|
||||
Index: index,
|
||||
Field: field,
|
||||
|
|
@ -727,7 +730,7 @@ func (c *InternalClient) BlockData(ctx context.Context, uri *pilosa.URI, index,
|
|||
return nil, nil, errors.Wrap(err, "marshaling")
|
||||
}
|
||||
|
||||
u := uriPathToURL(c.defaultURI, "/fragment/block/data")
|
||||
u := uriPathToURL(uri, "/fragment/block/data")
|
||||
req, err := http.NewRequest("GET", u.String(), bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "creating request")
|
||||
|
|
|
|||
|
|
@ -26,23 +26,10 @@ import (
|
|||
"github.com/pilosa/pilosa/http"
|
||||
"github.com/pilosa/pilosa/internal"
|
||||
"github.com/pilosa/pilosa/pql"
|
||||
"github.com/pilosa/pilosa/server"
|
||||
"github.com/pilosa/pilosa/test"
|
||||
)
|
||||
|
||||
func createCluster(c *pilosa.Cluster) ([]*test.Server, []*test.Holder) {
|
||||
numNodes := len(c.Nodes)
|
||||
hldr := make([]*test.Holder, numNodes)
|
||||
server := make([]*test.Server, numNodes)
|
||||
for i := 0; i < numNodes; i++ {
|
||||
hldr[i] = test.MustOpenHolder()
|
||||
server[i] = test.NewServer()
|
||||
server[i].Handler.API.Cluster = c
|
||||
server[i].Handler.API.Cluster.Nodes[i].URI = server[i].HostURI()
|
||||
server[i].Handler.API.Holder = hldr[i].Holder
|
||||
}
|
||||
return server, hldr
|
||||
}
|
||||
|
||||
var defaultClient *gohttp.Client
|
||||
|
||||
func init() {
|
||||
|
|
@ -52,39 +39,19 @@ func init() {
|
|||
|
||||
// Test distributed TopN Row count across 3 nodes.
|
||||
func TestClient_MultiNode(t *testing.T) {
|
||||
t.Skip() // Until test.NewServer() works
|
||||
c := test.MustRunCluster(t, 3,
|
||||
[]server.CommandOption{
|
||||
server.OptCommandServerOptions(pilosa.OptServerNodeID("node0"), pilosa.OptServerClusterHasher(&test.ModHasher{}))},
|
||||
[]server.CommandOption{
|
||||
server.OptCommandServerOptions(pilosa.OptServerNodeID("node1"), pilosa.OptServerClusterHasher(&test.ModHasher{}))},
|
||||
[]server.CommandOption{
|
||||
server.OptCommandServerOptions(pilosa.OptServerNodeID("node2"), pilosa.OptServerClusterHasher(&test.ModHasher{}))},
|
||||
)
|
||||
defer c.Close()
|
||||
|
||||
cluster := test.NewCluster(3)
|
||||
s, hldr := createCluster(cluster)
|
||||
|
||||
for i := 0; i < len(cluster.Nodes); i++ {
|
||||
defer hldr[i].Close()
|
||||
defer s[i].Close()
|
||||
}
|
||||
|
||||
s[0].Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, shards []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
|
||||
httpClient := http.NewInternalClientFromURI(&cluster.Nodes[0].URI, defaultClient)
|
||||
e := pilosa.NewExecutor(pilosa.OptExecutorInternalQueryClient(httpClient))
|
||||
e.Holder = hldr[0].Holder
|
||||
e.Node = cluster.Nodes[0]
|
||||
e.Cluster = cluster
|
||||
return e.Execute(ctx, index, query, shards, opt)
|
||||
}
|
||||
s[1].Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, shards []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
|
||||
httpClient := http.NewInternalClientFromURI(&cluster.Nodes[0].URI, defaultClient)
|
||||
e := pilosa.NewExecutor(pilosa.OptExecutorInternalQueryClient(httpClient))
|
||||
e.Holder = hldr[1].Holder
|
||||
e.Node = cluster.Nodes[1]
|
||||
e.Cluster = cluster
|
||||
return e.Execute(ctx, index, query, shards, opt)
|
||||
}
|
||||
s[2].Handler.Executor.ExecuteFn = func(ctx context.Context, index string, query *pql.Query, shards []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
|
||||
httpClient := http.NewInternalClientFromURI(&cluster.Nodes[0].URI, defaultClient)
|
||||
e := pilosa.NewExecutor(pilosa.OptExecutorInternalQueryClient(httpClient))
|
||||
e.Holder = hldr[2].Holder
|
||||
e.Node = cluster.Nodes[2]
|
||||
e.Cluster = cluster
|
||||
return e.Execute(ctx, index, query, shards, opt)
|
||||
hldr := []test.Holder{}
|
||||
for _, command := range c {
|
||||
hldr = append(hldr, test.Holder{Holder: command.Server.Holder()})
|
||||
}
|
||||
|
||||
// Create a dispersed set of bitmaps across 3 nodes such that each individual node and shard width increment would reveal a different TopN.
|
||||
|
|
@ -106,7 +73,7 @@ func TestClient_MultiNode(t *testing.T) {
|
|||
}
|
||||
}
|
||||
if !ownsNum {
|
||||
t.Fatalf("Trying to use shard %d on host %s, but it doesn't own that shard. It owns %v", num, s[i].Host(), owns)
|
||||
t.Fatalf("Trying to use shard %d on host %s, but it doesn't own that shard. It owns %v", num, c[i].URL(), owns)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,13 +87,21 @@ func TestClient_MultiNode(t *testing.T) {
|
|||
maxShard = x
|
||||
}
|
||||
}
|
||||
_, err := c[0].API.CreateIndex(context.Background(), "i", pilosa.IndexOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("creating index: %v", err)
|
||||
}
|
||||
_, err = c[0].API.CreateField(context.Background(), "i", "f", pilosa.OptFieldTypeSet(pilosa.DefaultCacheType, 100))
|
||||
if err != nil {
|
||||
t.Fatalf("creating field: %v", err)
|
||||
}
|
||||
|
||||
hldr[0].MustSetBits("i", "f", 100, baseBit0+10)
|
||||
hldr[0].MustSetBits("i", "f", 4, baseBit0+10, baseBit0+11, baseBit0+12)
|
||||
hldr[0].MustSetBits("i", "f", 4, baseBit0+10, baseBit0+11, baseBit0+12, baseBit0+13, baseBit0+14, baseBit0+15)
|
||||
hldr[0].MustSetBits("i", "f", 2, baseBit0+1, baseBit0+2, baseBit0+3, baseBit0+4)
|
||||
hldr[0].MustSetBits("i", "f", 3, baseBit0+1, baseBit0+2, baseBit0+3, baseBit0+4, baseBit0+5)
|
||||
hldr[0].MustSetBits("i", "f", 22, baseBit0+1, baseBit0+2, baseBit0+10)
|
||||
hldr[0].MustSetBits("i", "f", 22, baseBit0+1, baseBit0+2)
|
||||
|
||||
hldr[1].MustSetBits("i", "f", 99, baseBit1+1, baseBit1+2, baseBit1+3, baseBit1+4)
|
||||
hldr[1].MustSetBits("i", "f", 100, baseBit1+1, baseBit1+2, baseBit1+3, baseBit1+4, baseBit1+5, baseBit1+6, baseBit1+7, baseBit1+8, baseBit1+9, baseBit1+10)
|
||||
|
|
@ -145,39 +120,27 @@ func TestClient_MultiNode(t *testing.T) {
|
|||
// Rebuild the RankCache.
|
||||
// We have to do this to avoid the 10-second cache invalidation delay
|
||||
// built into cache.Invalidate()
|
||||
hldr[0].MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, shardNums[0]).RecalculateCache()
|
||||
hldr[1].MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, shardNums[1]).RecalculateCache()
|
||||
hldr[2].MustCreateRankedFragmentIfNotExists("i", "f", pilosa.ViewStandard, shardNums[2]).RecalculateCache()
|
||||
c[0].RecalculateCaches()
|
||||
c[1].RecalculateCaches()
|
||||
c[2].RecalculateCaches()
|
||||
|
||||
// Connect to each node to compare results.
|
||||
client := make([]*Client, 3)
|
||||
client[0] = MustNewClient(s[0].Host(), defaultClient)
|
||||
client[1] = MustNewClient(s[1].Host(), defaultClient)
|
||||
client[2] = MustNewClient(s[2].Host(), defaultClient)
|
||||
client[0] = MustNewClient(c[0].URL(), defaultClient)
|
||||
client[1] = MustNewClient(c[1].URL(), defaultClient)
|
||||
client[2] = MustNewClient(c[2].URL(), defaultClient)
|
||||
|
||||
topN := 4
|
||||
queryRequest := &internal.QueryRequest{
|
||||
Query: fmt.Sprintf(`TopN(f, n=%d)`, topN),
|
||||
Remote: false,
|
||||
}
|
||||
|
||||
result, err := client[0].Query(context.Background(), "i", queryRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check the results before every node has the correct max shard value.
|
||||
pairs := result.Results[0].Pairs
|
||||
for _, pair := range pairs {
|
||||
if pair.ID == 22 && pair.Count != 3 {
|
||||
t.Fatalf("Invalid Cluster wide MaxShard prevents accurate calculation of %s", pair)
|
||||
}
|
||||
}
|
||||
|
||||
// Set max shard to correct value.
|
||||
hldr[0].Index("i").SetRemoteMaxShard(maxShard)
|
||||
hldr[1].Index("i").SetRemoteMaxShard(maxShard)
|
||||
hldr[2].Index("i").SetRemoteMaxShard(maxShard)
|
||||
|
||||
result, err = client[0].Query(context.Background(), "i", queryRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -189,7 +152,7 @@ func TestClient_MultiNode(t *testing.T) {
|
|||
}
|
||||
p := []*internal.Pair{
|
||||
{ID: 100, Count: 12},
|
||||
{ID: 22, Count: 11},
|
||||
{ID: 22, Count: 10},
|
||||
{ID: 98, Count: 8},
|
||||
{ID: 99, Count: 7}}
|
||||
|
||||
|
|
|
|||
43
server.go
43
server.go
|
|
@ -70,6 +70,7 @@ type Server struct {
|
|||
diagnosticInterval time.Duration
|
||||
maxWritesPerRequest int
|
||||
isCoordinator bool
|
||||
syncer HolderSyncer
|
||||
|
||||
primaryTranslateStore TranslateStore
|
||||
|
||||
|
|
@ -209,6 +210,20 @@ func OptServerIsCoordinator(is bool) ServerOption {
|
|||
}
|
||||
}
|
||||
|
||||
func OptServerNodeID(nodeID string) ServerOption {
|
||||
return func(s *Server) error {
|
||||
s.nodeID = nodeID
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func OptServerClusterHasher(h Hasher) ServerOption {
|
||||
return func(s *Server) error {
|
||||
s.cluster.Hasher = h
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewServer returns a new instance of Server.
|
||||
func NewServer(opts ...ServerOption) (*Server, error) {
|
||||
s := &Server{
|
||||
|
|
@ -328,6 +343,12 @@ func (s *Server) Open() error {
|
|||
// buffered channel.
|
||||
s.cluster.listenForJoins()
|
||||
|
||||
s.syncer.Holder = s.holder
|
||||
s.syncer.Node = s.cluster.Node
|
||||
s.syncer.Cluster = s.cluster
|
||||
s.syncer.Closing = s.closing
|
||||
s.syncer.Stats = s.holder.Stats.WithTags("HolderSyncer")
|
||||
|
||||
// Start background monitoring.
|
||||
s.wg.Add(3)
|
||||
go func() { defer s.wg.Done(); s.monitorAntiEntropy() }()
|
||||
|
|
@ -370,12 +391,22 @@ func (s *Server) loadNodeID() string {
|
|||
return nodeID
|
||||
}
|
||||
|
||||
// SyncData manually invokes the anti entropy process which makes sure that this
|
||||
// node has the data from all replicas across the cluster.
|
||||
func (s *Server) SyncData() error {
|
||||
return errors.Wrap(s.syncer.SyncHolder(), "syncing holder")
|
||||
}
|
||||
|
||||
func (s *Server) monitorAntiEntropy() {
|
||||
if s.antiEntropyInterval == 0 {
|
||||
return // anti entropy disabled
|
||||
}
|
||||
ticker := time.NewTicker(s.antiEntropyInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
s.logger.Printf("holder sync monitor initializing (%s interval)", s.antiEntropyInterval)
|
||||
|
||||
// Initialize syncer with local holder and remote client.
|
||||
for {
|
||||
// Wait for tick or a close.
|
||||
select {
|
||||
|
|
@ -385,18 +416,10 @@ func (s *Server) monitorAntiEntropy() {
|
|||
s.holder.Stats.Count("AntiEntropy", 1, 1.0)
|
||||
}
|
||||
t := time.Now()
|
||||
s.logger.Printf("holder sync beginning")
|
||||
|
||||
// Initialize syncer with local holder and remote client.
|
||||
var syncer HolderSyncer
|
||||
syncer.Holder = s.holder
|
||||
syncer.Node = s.cluster.Node
|
||||
syncer.Cluster = s.cluster
|
||||
syncer.Closing = s.closing
|
||||
syncer.Stats = s.holder.Stats.WithTags("HolderSyncer")
|
||||
|
||||
// Sync holders.
|
||||
if err := syncer.SyncHolder(); err != nil {
|
||||
s.logger.Printf("holder sync beginning")
|
||||
if err := s.syncer.SyncHolder(); err != nil {
|
||||
s.logger.Printf("holder sync error: err=%s", err)
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright 2017 Pilosa Corp.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pilosa_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pilosa/pilosa"
|
||||
"github.com/pilosa/pilosa/server"
|
||||
"github.com/pilosa/pilosa/test"
|
||||
)
|
||||
|
||||
// TestMonitorAntiEntropy is a regression test which which caught a bug where
|
||||
// pilosa.Server was not having its remoteClient field set by an option and so
|
||||
// it was using a nil client in monitorAntiEntropy.
|
||||
func TestMonitorAntiEntropy(t *testing.T) {
|
||||
cluster := test.MustRunCluster(t, 3, []server.CommandOption{test.OptAntiEntropyInterval(time.Millisecond * 20)})
|
||||
client := cluster[1].Client()
|
||||
err := client.CreateIndex(context.Background(), "balh", pilosa.IndexOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("creating index: %v", err)
|
||||
}
|
||||
|
||||
err = client.CreateField(context.Background(), "balh", "fralh")
|
||||
if err != nil {
|
||||
t.Fatalf("creating field: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 40)
|
||||
for _, m := range cluster {
|
||||
err := m.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,6 +21,11 @@ import (
|
|||
"github.com/pilosa/pilosa"
|
||||
)
|
||||
|
||||
// modHasher represents a simple, mod-based hashing.
|
||||
type ModHasher struct{}
|
||||
|
||||
func (*ModHasher) Hash(key uint64, n int) int { return int(key) % n }
|
||||
|
||||
// NewCluster returns a cluster with n nodes and uses a mod-based hasher.
|
||||
func NewCluster(n int) *pilosa.Cluster {
|
||||
path, err := ioutil.TempDir("", "pilosa-cluster-")
|
||||
|
|
@ -30,7 +35,7 @@ func NewCluster(n int) *pilosa.Cluster {
|
|||
|
||||
c := pilosa.NewCluster()
|
||||
c.ReplicaN = 1
|
||||
c.Hasher = newModHasher()
|
||||
c.Hasher = &ModHasher{}
|
||||
c.Path = path
|
||||
c.Topology = pilosa.NewTopology()
|
||||
|
||||
|
|
@ -48,14 +53,6 @@ func NewCluster(n int) *pilosa.Cluster {
|
|||
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 }
|
||||
|
||||
// newURI is a test URI creator that intentionally swallows errors.
|
||||
func newURI(scheme, host string, port uint16) pilosa.URI {
|
||||
uri := pilosa.DefaultURI()
|
||||
|
|
|
|||
|
|
@ -121,6 +121,15 @@ func (h *Holder) Row(index, field string, rowID uint64) *pilosa.Row {
|
|||
return row
|
||||
}
|
||||
|
||||
func (h *Holder) RowAttrStore(index, field string) pilosa.AttrStore {
|
||||
idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{})
|
||||
f, err := idx.CreateFieldIfNotExists(field, pilosa.FieldOptions{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return f.RowAttrStore()
|
||||
}
|
||||
|
||||
// ViewRow returns a Row for a given field and view.
|
||||
func (h *Holder) ViewRow(index, field, view string, rowID uint64) *pilosa.Row {
|
||||
idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{})
|
||||
|
|
@ -142,7 +151,10 @@ func (h *Holder) SetBit(index, field string, rowID, columnID uint64) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
f.SetBit(rowID, columnID, nil)
|
||||
_, err = f.SetBit(rowID, columnID, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ClearBit clears a bit on the given field.
|
||||
|
|
@ -152,7 +164,10 @@ func (h *Holder) ClearBit(index, field string, rowID, columnID uint64) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
f.ClearBit(rowID, columnID)
|
||||
_, err = f.ClearBit(rowID, columnID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// MustSetBits sets columns on a row. Panic on error.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue