Skip to content

Commit 5c89e20

Browse files
committed
[raft] Improve /healthy status to return an error when raft errored
1 parent a77b08d commit 5c89e20

3 files changed

Lines changed: 36 additions & 1 deletion

File tree

cmds/core-service/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
auxs "github.com/interuss/dss/pkg/aux_/store"
2525
"github.com/interuss/dss/pkg/build"
2626
"github.com/interuss/dss/pkg/logging"
27+
"github.com/interuss/dss/pkg/raftstore"
2728
"github.com/interuss/dss/pkg/rid/application"
2829
rid_v1 "github.com/interuss/dss/pkg/rid/server/v1"
2930
rid_v2 "github.com/interuss/dss/pkg/rid/server/v2"
@@ -399,6 +400,15 @@ func RunHTTPServer(ctx context.Context, ctxCanceler func(), address, locality st
399400
func healthyEndpointMiddleware(logger *zap.Logger, next http.Handler) http.Handler {
400401
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
401402
if r.URL.Path == "/healthy" {
403+
404+
if params.GetStoreParameters().StoreType == params.RaftStoreType && !raftstore.IsHealthy() {
405+
w.WriteHeader(http.StatusServiceUnavailable)
406+
if _, err := w.Write([]byte("unhealthy: raft consensus is down")); err != nil {
407+
logger.Error("Error writing to /healthy")
408+
}
409+
return
410+
}
411+
402412
if _, err := w.Write([]byte("ok")); err != nil {
403413
logger.Error("Error writing to /healthy")
404414
}

pkg/raftstore/consensus/consensus.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/http"
99
"net/url"
1010
"sync"
11+
"sync/atomic"
1112
"time"
1213

1314
"github.com/interuss/dss/pkg/logging"
@@ -42,6 +43,10 @@ type Consensus struct {
4243
confState raftpb.ConfState
4344
snapshotIndex uint64
4445
appliedIndex uint64
46+
47+
// failed is set once handleReady exits, meaning this node's consensus
48+
// loop has stopped running and it can no longer process or apply entries.
49+
failed atomic.Bool
4550
}
4651

4752
func NewConsensus(ctx context.Context, logger *zap.Logger, connectParams params.ConnectParameters, provider snapshotProvider, commitC chan<- EntryCommit) (*Consensus, error) {
@@ -103,7 +108,8 @@ func NewConsensus(ctx context.Context, logger *zap.Logger, connectParams params.
103108
consensus.logger.Error("handleReady exited with error, shutting down consensus", zap.Error(err))
104109
}
105110

106-
consensus.Stop(context.Background())
111+
consensus.failed.Store(true)
112+
consensus.Stop(ctx)
107113
}()
108114

109115
return consensus, nil
@@ -126,6 +132,16 @@ func (c *Consensus) Stop(ctx context.Context) {
126132
})
127133
}
128134

135+
// IsHealthy reports whether this node's consensus loop is still running and
136+
// the cluster currently has an elected leader. A node with no known leader
137+
// has lost quorum (or hasn't joined yet) and cannot serve requests.
138+
func (c *Consensus) IsHealthy() bool {
139+
if c.failed.Load() {
140+
return false
141+
}
142+
return c.node.Status().Lead != 0
143+
}
144+
129145
// ProposeValue blocks until the proposal is committed and applied / dropped or until ctx is cancelled.
130146
func (c *Consensus) ProposeValue(ctx context.Context, requestType string, payload any, readOnly bool) (any, error) {
131147
proposal, err := newProposal(ctx, requestType, payload, readOnly)

pkg/raftstore/store.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,12 @@ func (s *Store[R]) processCommits(ctx context.Context, commitCh <-chan consensus
9595
}
9696
}
9797
}
98+
99+
// IsHealthy reports whether the shared raft consensus instance is up and has
100+
// an elected leader. Returns false if no raftstore has been initialized yet.
101+
func IsHealthy() bool {
102+
if sharedConsensus == nil {
103+
return false
104+
}
105+
return sharedConsensus.IsHealthy()
106+
}

0 commit comments

Comments
 (0)