This commit is contained in:
binwiederhier
2026-08-02 23:54:35 +02:00
parent 5a6c4277ad
commit ccfbc2309d
14 changed files with 459 additions and 221 deletions
+17 -13
View File
@@ -370,11 +370,14 @@ func New(conf *Config) (*Server, error) {
// be reached from the outside even before any firewalling.
func (s *Server) clusterHandler() http.Handler {
mux := http.NewServeMux()
// TODO(T8): reflect s.cluster.Healthy() here (and in handleHealth) instead of a static
// response, so health checks can pull a node that lost its registry heartbeat
mux.HandleFunc(apiHealthPath, func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
io.WriteString(w, `{"healthy":true}`+"\n")
if s.cluster.Healthy() {
io.WriteString(w, `{"healthy":true}`+"\n")
} else {
w.WriteHeader(http.StatusServiceUnavailable)
io.WriteString(w, `{"healthy":false}`+"\n")
}
})
mux.Handle("/", s.cluster)
return mux
@@ -396,15 +399,15 @@ func (s *Server) liveTopics() []string {
}
// topicAnnouncer returns the first-subscriber hook for a topic: it tells peer nodes right away
// that this node now wants messages for it (see Cluster.AnnounceTopics).
// that this node now wants messages for it (see Cluster.BroadcastState).
func (s *Server) topicAnnouncer(id string) func() {
return func() {
s.cluster.AnnounceTopics([]string{id})
s.cluster.BroadcastState(&cluster.State{AddedTopics: []string{id}})
}
}
// deliverFromBus delivers a message received from a peer node (via the cluster) to this
// node's local subscribers. It is the receive-side counterpart to Cluster.Relay: local
// node's local subscribers. It is the receive-side counterpart to Cluster.ForwardMessage: local
// delivery and all global side effects (Firebase, email, web push, upstream) already ran on the
// origin node, so this only publishes to the local topic, and never re-relays.
func (s *Server) deliverFromBus(m *model.Message) {
@@ -829,12 +832,13 @@ func (s *Server) handleTopicAuth(w http.ResponseWriter, _ *http.Request, _ *visi
}
func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
// TODO(T8): in cluster mode, reflect s.cluster.Healthy() so DNS/LB health checks stop
// routing NEW clients to a node whose peers no longer relay to it (see Cluster interface)
response := &apiHealthResponse{
Healthy: true,
// Unhealthy = the registry heartbeat went stale and peers stopped forwarding to this
// node; 503 lets status-code LB checks pull it (checkers must fail open, see cluster.Cluster)
healthy := s.cluster.Healthy()
if !healthy {
w.WriteHeader(http.StatusServiceUnavailable)
}
return s.writeJSON(w, response)
return s.writeJSON(w, &apiHealthResponse{Healthy: healthy})
}
// handleMetrics returns Prometheus metrics. This endpoint is only called if enable-metrics is set,
@@ -965,8 +969,8 @@ func (s *Server) dispatch(v *visitor, t *topic, m *model.Message, opts dispatchO
return err
}
}
// Relay to peer cluster nodes, whose subscribers do not show up in this node's topics map
if err := s.cluster.Relay(m); err != nil {
// ForwardMessage to peer cluster nodes, whose subscribers do not show up in this node's topics map
if err := s.cluster.ForwardMessage(m); err != nil {
logvm(v, m).Err(err).Warn("Cluster: unable to relay message to peer nodes")
}
// Fire the requested side-effect targets