diff --git a/api.go b/api.go index 19a433315..018eb8091 100644 --- a/api.go +++ b/api.go @@ -776,6 +776,12 @@ func (api *API) Hosts(ctx context.Context) []*Node { return api.cluster.Nodes() } +func (api *API) HostStates(ctx context.Context) map[string]string { + span, _ := tracing.StartSpanFromContext(ctx, "API.HostStates") + defer span.Finish() + return api.cluster.AllNodeStates() +} + // Node gets the ID, URI and coordinator status for this particular node. func (api *API) Node() *Node { node := api.server.node() @@ -1733,6 +1739,14 @@ func (api *API) State() string { return api.cluster.State() } +// ClusterName returns the cluster name. +func (api *API) ClusterName() string { + if api.cluster.Name == "" { + return api.cluster.id + } + return api.cluster.Name +} + // Version returns the Pilosa version. func (api *API) Version() string { return strings.TrimPrefix(Version, "v") @@ -1758,6 +1772,7 @@ func (api *API) Info() serverInfo { CPUType: si.CPUModel(), Memory: mem, TxSrc: api.holder.txf.TxType(), + ReplicaN: api.cluster.ReplicaN, } } @@ -2004,6 +2019,7 @@ func (api *API) TranslateFieldDB(ctx context.Context, indexName, fieldName strin type serverInfo struct { ShardWidth uint64 `json:"shardWidth"` + ReplicaN int `json:"replicaN"` Memory uint64 `json:"memory"` CPUType string `json:"cpuType"` CPUPhysicalCores int `json:"cpuPhysicalCores"` diff --git a/cluster.go b/cluster.go index 6e5450cf4..f31a819e5 100644 --- a/cluster.go +++ b/cluster.go @@ -208,6 +208,9 @@ type cluster struct { // nolint: maligned // The number of replicas a partition has. ReplicaN int + // Human-readable name of the cluster. + Name string + // Threshold for logging long-running queries // TODO(2.0) move this out of cluster. (why is it here??) longQueryTime time.Duration @@ -700,6 +703,12 @@ func (c *cluster) Nodes() []*Node { return ret } +func (c *cluster) AllNodeStates() map[string]string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.Topology.nodeStates +} + // removeNodeBasicSorted removes a node from the cluster, maintaining the sort // order. Returns true if the node was removed. unprotected. func (c *cluster) removeNodeBasicSorted(nodeID string) bool { diff --git a/ctl/server.go b/ctl/server.go index 968b2bd6d..221f3918f 100644 --- a/ctl/server.go +++ b/ctl/server.go @@ -47,6 +47,7 @@ func BuildServerFlags(cmd *cobra.Command, srv *server.Command) { 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. Only used for testing.") flags.DurationVarP((*time.Duration)(&srv.Config.Cluster.LongQueryTime), "cluster.long-query-time", "", time.Minute, "Duration that will trigger log and stat messages for slow queries.") + flags.StringVar(&srv.Config.Cluster.Name, "cluster.name", srv.Config.Cluster.Name, "Human-readable name for the cluster.") // Translation flags.StringVarP(&srv.Config.Translation.PrimaryURL, "translation.primary-url", "", srv.Config.Translation.PrimaryURL, "DEPRECATED: URL for primary translation node for replication.") diff --git a/http/handler.go b/http/handler.go index f000a365c..98634111d 100644 --- a/http/handler.go +++ b/http/handler.go @@ -381,8 +381,6 @@ func newRouter(handler *Handler) http.Handler { router.HandleFunc("/schema", handler.handleGetSchema).Methods("GET").Name("GetSchema") router.HandleFunc("/schema", handler.handlePostSchema).Methods("POST").Name("PostSchema") router.HandleFunc("/status", handler.handleGetStatus).Methods("GET").Name("GetStatus") - router.HandleFunc("/transaction", handler.handleGetTransactionList).Methods("GET").Name("GetTransactionList") - router.HandleFunc("/transaction/", handler.handleGetTransactionList).Methods("GET").Name("GetTransactionList") router.HandleFunc("/transaction", handler.handlePostTransaction).Methods("POST").Name("PostTransaction") router.HandleFunc("/transaction/", handler.handlePostTransaction).Methods("POST").Name("PostTransaction") router.HandleFunc("/transaction/{id}", handler.handleGetTransaction).Methods("GET").Name("GetTransaction") @@ -393,6 +391,8 @@ func newRouter(handler *Handler) http.Handler { router.HandleFunc("/version", handler.handleGetVersion).Methods("GET").Name("GetVersion") router.HandleFunc("/ui/usage", handler.handleGetUsage).Methods("GET").Name("GetUsage") + router.HandleFunc("/ui/transaction", handler.handleGetTransactionList).Methods("GET").Name("GetTransactionList") + router.HandleFunc("/ui/transaction/", handler.handleGetTransactionList).Methods("GET").Name("GetTransactionList") // /internal endpoints are for internal use only; they may change at any time. // DO NOT rely on these for external applications! @@ -700,9 +700,10 @@ func (h *Handler) handleGetStatus(w http.ResponseWriter, r *http.Request) { return } status := getStatusResponse{ - State: h.api.State(), - Nodes: h.api.Hosts(r.Context()), - LocalID: h.api.Node().ID, + State: h.api.State(), + Nodes: h.api.Hosts(r.Context()), + LocalID: h.api.Node().ID, + ClusterName: h.api.ClusterName(), } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(status); err != nil { @@ -758,9 +759,10 @@ type getSchemaResponse struct { } type getStatusResponse struct { - State string `json:"state"` - Nodes []*pilosa.Node `json:"nodes"` - LocalID string `json:"localID"` + State string `json:"state"` + Nodes []*pilosa.Node `json:"nodes"` + LocalID string `json:"localID"` + ClusterName string `json:"clusterName"` } func hash(s string) string { diff --git a/server.go b/server.go index a93df180a..a011ab0ba 100644 --- a/server.go +++ b/server.go @@ -269,6 +269,14 @@ func OptServerClusterDisabled(disabled bool, hosts []string) ServerOption { } } +// OptServerClusterName sets the human-readable cluster name. +func OptServerClusterName(name string) ServerOption { + return func(s *Server) error { + s.cluster.Name = name + return nil + } +} + // OptServerSerializer is a functional option on Server // used to set the serializer. func OptServerSerializer(ser Serializer) ServerOption { diff --git a/server/config.go b/server/config.go index c51a7f95f..3e8575c81 100644 --- a/server/config.go +++ b/server/config.go @@ -122,6 +122,7 @@ type Config struct { Coordinator bool `toml:"coordinator"` ReplicaN int `toml:"replicas"` Hosts []string `toml:"hosts"` + Name string `toml:"name"` // TODO(2.0) move this out of cluster. (why is it here??) LongQueryTime toml.Duration `toml:"long-query-time"` } `toml:"cluster"` diff --git a/server/server.go b/server/server.go index 1b7dea316..077c8a55c 100644 --- a/server/server.go +++ b/server/server.go @@ -406,6 +406,7 @@ func (m *Command) SetupServer() error { pilosa.OptServerGRPCURI(advertiseGRPCURI), pilosa.OptServerInternalClient(http.NewInternalClientFromURI(uri, c)), pilosa.OptServerClusterDisabled(m.Config.Cluster.Disabled, m.Config.Cluster.Hosts), + pilosa.OptServerClusterName(m.Config.Cluster.Name), pilosa.OptServerSerializer(proto.Serializer{}), pilosa.OptServerTxsrc(m.Config.Txsrc), coordinatorOpt,