DefaultClusterType = ClusterGossip

This commit removes the `httpbroadcast` NodeSet, sets the default cluster type
to `gossip`, and uses the `static` cluster type for most test cases.
This commit is contained in:
Travis 2017-07-26 09:16:39 -05:00
parent 40b0235552
commit a3b1b2ee1a
No known key found for this signature in database
GPG key ID: 7F08008DFD9314C9
16 changed files with 138 additions and 354 deletions

View file

@ -38,8 +38,7 @@ const (
// Node represents a node in the cluster.
type Node struct {
Host string `json:"host"`
InternalHost string `json:"internalHost"`
Host string `json:"host"`
status *internal.NodeStatus `json:"status"`
}

View file

@ -22,7 +22,6 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/pilosa/pilosa"
"github.com/pilosa/pilosa/httpbroadcast"
"github.com/pilosa/pilosa/test"
)
@ -110,10 +109,10 @@ func TestCluster_NodeStates(t *testing.T) {
{Host: "serverB:1000"},
{Host: "serverC:1000"},
},
NodeSet: &httpbroadcast.HTTPNodeSet{},
NodeSet: &pilosa.StaticNodeSet{},
}
err := c.NodeSet.(*httpbroadcast.HTTPNodeSet).Join([]*pilosa.Node{
err := c.NodeSet.(*pilosa.StaticNodeSet).Join([]*pilosa.Node{
&pilosa.Node{Host: "serverA:1000"},
&pilosa.Node{Host: "serverC:1000"},
&pilosa.Node{Host: "serverD:1000"},

View file

@ -96,7 +96,7 @@ func (v *validator) Check(actual, expected interface{}) {
// Error returns the validator's error value if any v.Check call found an error.
func (v *validator) Error() error { return v.err }
// commandTest represents all possible ways to configure a a pilosa command, as
// commandTest represents all possible ways to configure a pilosa command, as
// well as a function for validating whether the command worked as expected.
// args should be set to everything that comes after "pilosa" on the comand
// line. See tests like backup_test.go for examples.

View file

@ -52,15 +52,11 @@ func TestServerConfig(t *testing.T) {
[cluster]
poll-interval = "45s"
type = "http"
type = "static"
replicas = 2
hosts = [
"localhost:19444",
]
internal-hosts = [
"localhost:19500",
"localhost:19501",
]
`,
validation: func() error {
v := validator{}
@ -80,14 +76,10 @@ func TestServerConfig(t *testing.T) {
bind = "localhost:0"
data-dir = "` + actualDataDir + `"
[cluster]
type = "http"
type = "static"
hosts = [
"localhost:19444",
]
internal-hosts = [
"localhost:19500",
"localhost:19501",
]
[plugins]
path = "/var/sloth"
`,
@ -101,7 +93,7 @@ func TestServerConfig(t *testing.T) {
},
// TEST 2
{
args: []string{"server", "--log-path", logFile.Name()},
args: []string{"server", "--log-path", logFile.Name(), "--cluster.type", "static"},
env: map[string]string{"PILOSA_PROFILE.CPU_TIME": "1m"},
cfgFileContent: `
bind = "localhost:19444"

View file

@ -20,7 +20,6 @@ import "time"
const (
ClusterNone = ""
ClusterStatic = "static"
ClusterHTTP = "http"
ClusterGossip = "gossip"
)
@ -32,12 +31,12 @@ const (
DefaultPort = "10101"
// DefaultClusterType sets the node intercommunication method.
DefaultClusterType = ClusterStatic
DefaultClusterType = ClusterGossip
// DefaultInternalPort the port the nodes intercommunicate on.
DefaultInternalPort = "14000"
// DefaultGossipPort indicates the port to which pilosa should bind for internal state sharing.
DefaultGossipPort = "14000"
// DefaultMetrics sets the internal metrics to no op
// DefaultMetrics sets the internal metrics to no-op.
DefaultMetrics = "nop"
// DefaultMaxWritesPerRequest is the default number of writes per request.
@ -45,21 +44,20 @@ const (
)
// ClusterTypes set of cluster types.
var ClusterTypes = []string{ClusterNone, ClusterStatic, ClusterHTTP, ClusterGossip}
var ClusterTypes = []string{ClusterNone, ClusterStatic, ClusterGossip}
// Config represents the configuration for the command.
type Config struct {
DataDir string `toml:"data-dir"`
Bind string `toml:"bind"`
InternalPort string `toml:"internal-port"`
DataDir string `toml:"data-dir"`
Bind string `toml:"bind"`
GossipPort string `toml:"gossip-port"`
GossipSeed string `toml:"gossip-seed"`
Cluster struct {
ReplicaN int `toml:"replicas"`
Type string `toml:"type"`
Hosts []string `toml:"hosts"`
InternalHosts []string `toml:"internal-hosts"`
PollInterval Duration `toml:"poll-interval"`
GossipSeed string `toml:"gossip-seed"`
LongQueryTime Duration `toml:"long-query-time"`
} `toml:"cluster"`
@ -94,7 +92,6 @@ func NewConfig() *Config {
c.Cluster.Type = DefaultClusterType
c.Cluster.PollInterval = Duration(DefaultPollingInterval)
c.Cluster.Hosts = []string{}
c.Cluster.InternalHosts = []string{}
c.AntiEntropy.Interval = Duration(DefaultAntiEntropyInterval)
c.Metric.Service = DefaultMetrics
return c
@ -105,31 +102,12 @@ func (c *Config) Validate() error {
if !StringInSlice(c.Cluster.Type, ClusterTypes) {
return ErrConfigClusterTypeInvalid
}
if len(c.Cluster.Hosts) > 1 && !(c.Cluster.Type == ClusterHTTP || c.Cluster.Type == ClusterGossip) {
return ErrConfigClusterTypeMissing
}
if c.Cluster.Type == ClusterHTTP || c.Cluster.Type == ClusterGossip {
if c.Cluster.ReplicaN > len(c.Cluster.Hosts) {
return ErrConfigReplicaNInvalid
}
if c.Cluster.Type == ClusterGossip {
if !foundItem(c.Cluster.Hosts, c.Bind) {
return ErrConfigHostsMissing
}
}
if c.Cluster.Type == ClusterHTTP {
if len(c.Cluster.Hosts) != len(c.Cluster.InternalHosts) {
return ErrConfigHostsMismatch
}
// TODO: this seems like an odd check; it's just ensuring that InternalPort
// matches any one substring from any of the InternalHosts.
// I suggest we either remove this completely or make it actually check
// the port portion of the address for this node. (note that this only applies
// to the http broadcaster, so if we simply use gossip for all implementations
// we can remove this).
if !ContainsSubstring(c.InternalPort, c.Cluster.InternalHosts) {
return ErrConfigBroadcastPort
}
}
return nil
}

View file

@ -12,42 +12,25 @@ func Test_NewConfig(t *testing.T) {
c := pilosa.NewConfig()
c.Cluster.Hosts = []string{c.Bind, "localhost:10102"}
if err := c.Validate(); err != pilosa.ErrConfigClusterTypeMissing {
t.Fatal(err)
}
c.Cluster.Type = "test"
// Change cluster type from the default (gossip) to an invalid string.
c.Cluster.Type = "invalid-type"
if err := c.Validate(); err != pilosa.ErrConfigClusterTypeInvalid {
t.Fatal(err)
}
c.Cluster.Type = pilosa.ClusterHTTP
if err := c.Validate(); err != pilosa.ErrConfigHostsMismatch {
t.Fatal(err)
}
// Change cluster type back to gossip.
c.Cluster.Type = pilosa.ClusterGossip
c.InternalPort = pilosa.DefaultInternalPort
c.Cluster.InternalHosts = []string{"localhost:14004", "localhost:14001"}
if err := c.Validate(); err != pilosa.ErrConfigBroadcastPort {
t.Fatal(err)
}
c.Cluster.InternalHosts = []string{"localhost:14000", "localhost:14001"}
// Check for bind address in cluster hosts.
c.Bind = "localhost:1"
// Check for bind addres in cluster hosts
if err := c.Validate(); err != pilosa.ErrConfigHostsMissing {
t.Fatal(err)
}
c.Bind = "localhost:10101"
c.Cluster.ReplicaN = 3
if err := c.Validate(); err != pilosa.ErrConfigReplicaNInvalid {
t.Fatal(err)
}
c.Cluster.ReplicaN = 2
c.Cluster.Type = pilosa.ClusterGossip
c.Cluster.GossipSeed = "localhost:14000"
c.GossipSeed = "localhost:14000"
if err := c.Validate(); err != nil {
t.Fatal(err)
}

View file

@ -26,11 +26,11 @@ func BuildServerFlags(cmd *cobra.Command, srv *server.Command) {
flags := cmd.Flags()
flags.StringVarP(&srv.Config.DataDir, "data-dir", "d", "~/.pilosa", "Directory to store pilosa data files.")
flags.StringVarP(&srv.Config.Bind, "bind", "b", ":10101", "Default URI on which pilosa should listen.")
flags.StringVarP(&srv.Config.InternalPort, "internal-port", "", "", "Port to which pilosa should bind for internal state sharing.")
flags.StringVarP(&srv.Config.GossipPort, "gossip-port", "", "", "Port to which pilosa should bind for internal state sharing.")
flags.StringVarP(&srv.Config.GossipSeed, "gossip-seed", "", "", "Host with which to seed the gossip membership.")
flags.IntVarP(&srv.Config.MaxWritesPerRequest, "max-writes-per-request", "", srv.Config.MaxWritesPerRequest, "Number of write commands per request.")
flags.IntVarP(&srv.Config.Cluster.ReplicaN, "cluster.replicas", "", 1, "Number of hosts each piece of data should be stored on.")
flags.StringSliceVarP(&srv.Config.Cluster.Hosts, "cluster.hosts", "", []string{}, "Comma separated list of hosts in cluster.")
flags.StringSliceVarP(&srv.Config.Cluster.InternalHosts, "cluster.internal-hosts", "", []string{}, "Comma separated list of hosts in cluster used for internal communication.")
flags.DurationVarP((*time.Duration)(&srv.Config.Cluster.PollInterval), "cluster.poll-interval", "", time.Minute, "Polling interval for cluster.") // TODO what actually is this?
flags.DurationVarP((*time.Duration)(&srv.Config.Cluster.LongQueryTime), "cluster.long-query-time", "", time.Minute, "Long Query Time.")
flags.StringVarP(&srv.Config.Plugins.Path, "plugins.path", "", "", "Path to plugin directory.")
@ -38,8 +38,7 @@ func BuildServerFlags(cmd *cobra.Command, srv *server.Command) {
flags.DurationVarP((*time.Duration)(&srv.Config.AntiEntropy.Interval), "anti-entropy.interval", "", time.Minute*10, "Interval at which to run anti-entropy routine.")
flags.StringVarP(&srv.CPUProfile, "profile.cpu", "", "", "Where to store CPU profile.")
flags.DurationVarP(&srv.CPUTime, "profile.cpu-time", "", 30*time.Second, "CPU profile duration.")
flags.StringVarP(&srv.Config.Cluster.Type, "cluster.type", "", "static", "Determine how the cluster handles membership and state sharing. Choose from [static, http, gossip]")
flags.StringVarP(&srv.Config.Cluster.GossipSeed, "cluster.gossip-seed", "", "", "Host with which to seed the gossip membership.")
flags.StringVarP(&srv.Config.Cluster.Type, "cluster.type", "", "gossip", "Determine how the cluster handles membership and state sharing. Choose from [static, gossip]")
flags.StringVarP(&srv.Config.Metric.Service, "metric.service", "", "nop", "Default URI on which pilosa should listen.")
flags.StringVarP(&srv.Config.Metric.Host, "metric.host", "", "", "Default URI to send metrics.")
flags.DurationVarP((*time.Duration)(&srv.Config.Metric.PollInterval), "metric.poll-interval", "", time.Minute*0, "Polling interval metrics.")

View file

@ -28,13 +28,13 @@ func TestBuildServerFlags(t *testing.T) {
stdin, stdout, stderr := GetIO(buf)
Server := server.NewCommand(stdin, stdout, stderr)
BuildServerFlags(cm, Server)
if cm.Flags().Lookup("internal-port").Name == "" {
t.Fatal("internal-port flag is missed ")
if cm.Flags().Lookup("gossip-port").Name == "" {
t.Fatal("gossip-port flag is required")
}
if cm.Flags().Lookup("data-dir").Name == "" {
t.Fatal("data-dir flag is missed ")
t.Fatal("data-dir flag is required")
}
if cm.Flags().Lookup("log-path").Name == "" {
t.Fatal("log-path is missed ")
t.Fatal("log-path flag is required")
}
}

View file

@ -352,7 +352,7 @@ curl -XGET localhost:10101/hosts
Response:
```
[{"host":":10101","internalHost":""}]
[{"host":":10101"}]
```
### Get version

View file

@ -58,15 +58,15 @@ Any flag that has a value that is a comma separated list on the command line bec
bind = localhost:10101
```
#### Internal Port
#### Gossip Port
* Description: Port to which Pilosa should bind for internal communication.
* Flag: `--internal-port=11101`
* Env: `PILOSA_INTERNAL_PORT=11101`
* Flag: `--gossip-port=11101`
* Env: `PILOSA_GOSSIP_PORT=11101`
* Config:
```toml
internal-port = 11101
gossip-port = 11101
```
#### Cluster Hosts
@ -81,18 +81,6 @@ Any flag that has a value that is a comma separated list on the command line bec
hosts = ["localhost:10101"]
```
#### Cluster Internal Hosts
* Description: List of hosts in the cluster used for internal communication. Multiple hosts should be comma separated in the flag and env forms.
* Flag: `--cluster.internal-hosts="localhost:11101"`
* Env: `PILOSA_CLUSTER.INTERNAL_HOSTS="localhost:11101"`
* Config:
```toml
[cluster]
internal-hosts = ["localhost:11101"]
```
#### Cluster Poll Interval
* Description: Polling interval for cluster.

View file

@ -18,6 +18,7 @@ import (
"fmt"
"io"
"log"
"time"
"golang.org/x/sync/errgroup"
@ -71,7 +72,7 @@ func (g *GossipNodeSet) Open() error {
// attach to gossip seed node
nodes := []*pilosa.Node{&pilosa.Node{Host: g.config.gossipSeed}} //TODO: support a list of seeds
_, err = g.memberlist.Join(pilosa.Nodes(nodes).Hosts())
err = g.joinWithRetry(pilosa.Nodes(nodes).Hosts())
if err != nil {
return err
}
@ -84,6 +85,31 @@ func (g *GossipNodeSet) Open() error {
return nil
}
// joinWithRetry wraps the standard memberlist Join function in a retry.
func (g *GossipNodeSet) joinWithRetry(hosts []string) error {
err := retry(60, 2*time.Second, func() error {
_, err := g.memberlist.Join(hosts)
return err
})
return err
}
// retry periodically retries function fn a specified number of attempts.
func retry(attempts int, sleep time.Duration, fn func() error) (err error) {
for i := 0; ; i++ {
err = fn()
if err == nil {
return
}
if i >= (attempts - 1) {
break
}
time.Sleep(sleep)
log.Println("retrying after error:", err)
}
return fmt.Errorf("after %d attempts, last error: %s", attempts, err)
}
// logger returns a logger for the GossipNodeSet.
func (g *GossipNodeSet) logger() *log.Logger {
return log.New(g.LogOutput, "", log.LstdFlags)

View file

@ -942,7 +942,7 @@ func TestHandler_Fragment_Nodes(t *testing.T) {
h.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("unexpected status code: %d", w.Code)
} else if w.Body.String() != `[{"host":"host2","internalHost":""},{"host":"host0","internalHost":""}]`+"\n" {
} else if w.Body.String() != `[{"host":"host2"},{"host":"host0"}]`+"\n" {
t.Fatalf("unexpected body: %q", w.Body.String())
}
}

View file

@ -1,201 +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 httpbroadcast
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"golang.org/x/sync/errgroup"
"github.com/gogo/protobuf/proto"
"github.com/pilosa/pilosa"
)
// HTTPBroadcaster represents a NodeSet that broadcasts messages over HTTP.
type HTTPBroadcaster struct {
server *pilosa.Server
internalPort string
}
// NewHTTPBroadcaster returns a new instance of HTTPBroadcaster.
func NewHTTPBroadcaster(s *pilosa.Server, internalPort string) *HTTPBroadcaster {
return &HTTPBroadcaster{server: s, internalPort: internalPort}
}
// SendSync sends a protobuf message to all nodes simultaneously.
// It waits for all nodes to respond before the function returns (and returns any errors).
func (h *HTTPBroadcaster) SendSync(pb proto.Message) error {
// Marshal the pb to []byte
buf, err := pilosa.MarshalMessage(pb)
if err != nil {
return err
}
nodes, err := h.nodes()
if err != nil {
return err
}
var g errgroup.Group
for _, n := range nodes {
// Don't send the message to the local node.
if n.Host == h.server.Host {
continue
}
node := n
g.Go(func() error {
return h.sendNodeMessage(node, buf)
})
}
return g.Wait()
}
// SendAsync exists to implement the Broadcaster interface, but just calls
// SendSync.
func (h *HTTPBroadcaster) SendAsync(pb proto.Message) error {
return h.SendSync(pb)
}
func (h *HTTPBroadcaster) nodes() ([]*pilosa.Node, error) {
if h.server == nil {
return nil, errors.New("HTTPBroadcaster has no reference to Server")
}
nodeset, ok := h.server.Cluster.NodeSet.(*HTTPNodeSet)
if !ok {
return nil, errors.New("NodeSet cannot be caste to HTTPNodeSet")
}
return nodeset.Nodes(), nil
}
func (h *HTTPBroadcaster) sendNodeMessage(node *pilosa.Node, msg []byte) error {
var client *http.Client
client = http.DefaultClient
// Create HTTP request.
req, err := http.NewRequest("POST", (&url.URL{
Scheme: "http",
Host: node.InternalHost,
}).String(), bytes.NewReader(msg))
// Require protobuf encoding.
req.Header.Set("Content-Type", "application/x-protobuf")
req.Header.Set("User-Agent", "pilosa/"+pilosa.Version)
// Send request to remote node.
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// Read response into buffer.
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// Check status code.
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("invalid status sendNodeMessage: code=%d, err=%s, req=%v", resp.StatusCode, body, req)
}
return nil
}
// HTTPBroadcastReceiver unmarshals incoming messages over HTTP and passes them on to the handler.
type HTTPBroadcastReceiver struct {
port string
handler pilosa.BroadcastHandler
logOutput io.Writer
}
// NewHTTPBroadcastReceiver returns a new instance of HTTPBroadcastReceiver.
func NewHTTPBroadcastReceiver(port string, logOutput io.Writer) *HTTPBroadcastReceiver {
return &HTTPBroadcastReceiver{
port: port,
logOutput: logOutput,
}
}
// Start implements the BroadcastReceiver interface and starts listening for broadcast messages.
func (rec *HTTPBroadcastReceiver) Start(b pilosa.BroadcastHandler) error {
rec.handler = b
go func() {
err := http.ListenAndServe(":"+rec.port, rec)
if err != nil {
fmt.Fprintf(rec.logOutput, "Error listening on %v for HTTPBroadcastReceiver: %v\n", ":"+rec.port, err)
}
}()
return nil
}
func (rec *HTTPBroadcastReceiver) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Content-Type") != "application/x-protobuf" {
http.Error(w, "Unsupported media type", http.StatusUnsupportedMediaType)
return
}
// Read entire body.
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Unmarshal message to specific proto type.
m, err := pilosa.UnmarshalMessage(body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := rec.handler.ReceiveMessage(m); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
// HTTPNodeSet represents a NodeSet that broadcasts messages over HTTP.
type HTTPNodeSet struct {
nodes []*pilosa.Node
}
// NewHTTPNodeSet returns a new instance of HTTPNodeSet.
func NewHTTPNodeSet() *HTTPNodeSet {
return &HTTPNodeSet{}
}
// Nodes implements the NodeSet interface and returns a list of nodes in the cluster.
func (h *HTTPNodeSet) Nodes() []*pilosa.Node {
return h.nodes
}
// Open implements the NodeSet interface to start network activity, but for a HTTPNodeSet it does nothing.
func (h *HTTPNodeSet) Open() error {
return nil
}
// Join sets the NodeSet nodes to the slice of Nodes passed in.
func (h *HTTPNodeSet) Join(nodes []*pilosa.Node) error {
h.nodes = nodes
return nil
}

View file

@ -71,11 +71,7 @@ var (
ErrTooManyWrites = errors.New("too many write commands")
ErrConfigClusterTypeInvalid = errors.New("invalid cluster type")
ErrConfigClusterTypeMissing = errors.New("missing cluster type")
ErrConfigHostsMissing = errors.New("missing bind address in cluster hosts")
ErrConfigBroadcastPort = errors.New("internal-port not found in internal-hosts")
ErrConfigHostsMismatch = errors.New("hosts and internal-hosts length mismatch")
ErrConfigReplicaNInvalid = errors.New("replica number must be <= hosts")
)
// Regular expression to validate index and frame names.

View file

@ -32,7 +32,6 @@ import (
"github.com/pilosa/pilosa"
"github.com/pilosa/pilosa/gossip"
"github.com/pilosa/pilosa/httpbroadcast"
"github.com/pilosa/pilosa/statsd"
)
@ -105,7 +104,7 @@ func (m *Command) Run(args ...string) (err error) {
return nil
}
// SetupServer use the cluster configuration to setup this server
// SetupServer uses the cluster configuration to set up this server.
func (m *Command) SetupServer() error {
err := m.Config.Validate()
if err != nil {
@ -118,12 +117,6 @@ func (m *Command) SetupServer() error {
for _, hostport := range m.Config.Cluster.Hosts {
cluster.Nodes = append(cluster.Nodes, &pilosa.Node{Host: hostport})
}
// TODO: if InternalHosts is not provided then pilosa.Node.InternalHost is empty.
// This will throw an error when trying to Broadcast messages over HTTP.
// One option may be to fall back to using host from hostport + config.InternalPort.
for i, internalhostport := range m.Config.Cluster.InternalHosts {
cluster.Nodes[i].InternalHost = internalhostport
}
m.Server.Cluster = cluster
// Setup logging output.
@ -152,28 +145,20 @@ func (m *Command) SetupServer() error {
}
// Set internal port (string).
internalPortStr := pilosa.DefaultInternalPort
if m.Config.InternalPort != "" {
internalPortStr = m.Config.InternalPort
gossipPortStr := pilosa.DefaultGossipPort
if m.Config.GossipPort != "" {
gossipPortStr = m.Config.GossipPort
}
switch m.Config.Cluster.Type {
case pilosa.ClusterHTTP:
m.Server.Broadcaster = httpbroadcast.NewHTTPBroadcaster(m.Server, internalPortStr)
m.Server.BroadcastReceiver = httpbroadcast.NewHTTPBroadcastReceiver(internalPortStr, m.Server.LogOutput)
m.Server.Cluster.NodeSet = httpbroadcast.NewHTTPNodeSet()
err := m.Server.Cluster.NodeSet.(*httpbroadcast.HTTPNodeSet).Join(m.Server.Cluster.Nodes)
if err != nil {
return err
}
case pilosa.ClusterGossip:
gossipPort, err := strconv.Atoi(internalPortStr)
gossipPort, err := strconv.Atoi(gossipPortStr)
if err != nil {
return err
}
gossipSeed := pilosa.DefaultHost
if m.Config.Cluster.GossipSeed != "" {
gossipSeed = m.Config.Cluster.GossipSeed
if m.Config.GossipSeed != "" {
gossipSeed = m.Config.GossipSeed
}
// get the host portion of addr to use for binding
gossipHost, _, err := net.SplitHostPort(m.Config.Bind)

View file

@ -33,10 +33,11 @@ import (
"strings"
"testing"
"testing/quick"
"time"
"github.com/BurntSushi/toml"
"github.com/pilosa/pilosa"
"github.com/pilosa/pilosa/httpbroadcast"
"github.com/pilosa/pilosa/gossip"
"github.com/pilosa/pilosa/server"
"github.com/pilosa/pilosa/test"
)
@ -426,38 +427,73 @@ func TestMain_SendReceiveMessage(t *testing.T) {
// Update cluster config
m0.Server.Cluster.Nodes = []*pilosa.Node{
{Host: m0.Server.Host, InternalHost: "localhost:" + freePorts[0]},
{Host: m1.Server.Host, InternalHost: "localhost:" + freePorts[1]},
{Host: m0.Server.Host},
{Host: m1.Server.Host},
}
m1.Server.Cluster.Nodes = m0.Server.Cluster.Nodes
// Configure node0
m0.Server.Broadcaster = httpbroadcast.NewHTTPBroadcaster(m0.Server, freePorts[0])
m0.Server.Handler.Broadcaster = m0.Server.Broadcaster
m0.Server.Holder.Broadcaster = m0.Server.Broadcaster
m0.Server.BroadcastReceiver = httpbroadcast.NewHTTPBroadcastReceiver(freePorts[0], nil)
m0.Server.Cluster.NodeSet = httpbroadcast.NewHTTPNodeSet()
err = m0.Server.Cluster.NodeSet.(*httpbroadcast.HTTPNodeSet).Join(m0.Server.Cluster.Nodes)
// get the host portion of addr to use for binding
gossipHost, _, err := net.SplitHostPort(m0.Server.Host)
if err != nil {
gossipHost = m0.Server.Host
}
if gossipHost == "localhost" {
gossipHost = "127.0.0.1"
}
gossipPort, err := strconv.Atoi(freePorts[0])
if err != nil {
t.Fatal(err)
}
gossipSeed := gossipHost + ":" + freePorts[0]
gossipNodeSet0 := gossip.NewGossipNodeSet(m0.Server.Host, gossipHost, gossipPort, gossipSeed, m0.Server)
m0.Server.Cluster.NodeSet = gossipNodeSet0
m0.Server.Broadcaster = gossipNodeSet0
m0.Server.Handler.Broadcaster = m0.Server.Broadcaster
m0.Server.Holder.Broadcaster = m0.Server.Broadcaster
m0.Server.BroadcastReceiver = gossipNodeSet0
if err := m0.Server.BroadcastReceiver.Start(m0.Server); err != nil {
t.Fatal(err)
}
// Open NodeSet communication
if err := m0.Server.Cluster.NodeSet.Open(); err != nil {
t.Fatal(err)
}
// Configure node1
m1.Server.Broadcaster = httpbroadcast.NewHTTPBroadcaster(m1.Server, freePorts[1])
m1.Server.Handler.Broadcaster = m1.Server.Broadcaster
m1.Server.Holder.Broadcaster = m1.Server.Broadcaster
m1.Server.BroadcastReceiver = httpbroadcast.NewHTTPBroadcastReceiver(freePorts[1], nil)
m1.Server.Cluster.NodeSet = httpbroadcast.NewHTTPNodeSet()
err = m1.Server.Cluster.NodeSet.(*httpbroadcast.HTTPNodeSet).Join(m1.Server.Cluster.Nodes)
// get the host portion of addr to use for binding
gossipHost, _, err = net.SplitHostPort(m1.Server.Host)
if err != nil {
gossipHost = m1.Server.Host
}
if gossipHost == "localhost" {
gossipHost = "127.0.0.1"
}
gossipPort, err = strconv.Atoi(freePorts[1])
if err != nil {
t.Fatal(err)
}
gossipNodeSet1 := gossip.NewGossipNodeSet(m1.Server.Host, gossipHost, gossipPort, gossipSeed, m1.Server)
m1.Server.Cluster.NodeSet = gossipNodeSet1
m1.Server.Broadcaster = gossipNodeSet1
m1.Server.Handler.Broadcaster = m1.Server.Broadcaster
m1.Server.Holder.Broadcaster = m1.Server.Broadcaster
m1.Server.BroadcastReceiver = gossipNodeSet1
if err := m1.Server.BroadcastReceiver.Start(m1.Server); err != nil {
t.Fatal(err)
}
// Open NodeSet communication
if err := m1.Server.Cluster.NodeSet.Open(); err != nil {
t.Fatal(err)
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Expected indexes and Frames
expected := map[string][]string{
@ -509,12 +545,15 @@ func TestMain_SendReceiveMessage(t *testing.T) {
// Write data on first node.
if _, err := m0.Query("i", "", `
SetBit(rowID=1, frame="f", columnID=1)
SetBit(rowID=1, frame="f", columnID=2400000)
`); err != nil {
SetBit(rowID=1, frame="f", columnID=1)
SetBit(rowID=1, frame="f", columnID=2400000)
`); err != nil {
t.Fatal(err)
}
// We have to wait for the broadcast message to be sent before checking state.
time.Sleep(1 * time.Second)
// Make sure node0 knows about the latest MaxSlice.
maxSlices0, err := client0.MaxSliceByIndex(context.Background())
if err != nil {
@ -535,15 +574,15 @@ func TestMain_SendReceiveMessage(t *testing.T) {
// Write input definition to the first node.
if _, err := m0.CreateDefinition("i", "test", `{
"frames": [{"name": "event-time",
"options": {
"cacheType": "ranked",
"timeQuantum": "YMD"
}}],
"fields": [{"name": "columnID",
"primaryKey": true
}]}
`); err != nil {
"frames": [{"name": "event-time",
"options": {
"cacheType": "ranked",
"timeQuantum": "YMD"
}}],
"fields": [{"name": "columnID",
"primaryKey": true
}]}
`); err != nil {
t.Fatal(err)
}
@ -607,6 +646,7 @@ func NewMain() *Main {
m.Server.Network = *test.Network
m.Config.DataDir = path
m.Config.Bind = "localhost:0"
m.Config.Cluster.Type = "static"
m.Command.Stdin = &m.Stdin
m.Command.Stdout = &m.Stdout
m.Command.Stderr = &m.Stderr