-
Notifications
You must be signed in to change notification settings - Fork 272
chore(preconf): enrich sequencer server health check impl. #3086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: preconf-dev
Are you sure you want to change the base?
Changes from 6 commits
c86e80a
43afcd9
bc71802
57835ec
b488929
4b0a2be
a1935f5
d3931ad
31d83f9
ef25538
068b8f0
ec21af0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,13 +59,29 @@ type PayloadProvider interface { | |
| GetPayloadBySlot(ctx context.Context, slot math.Slot, parentBlockRoot common.Root) (ctypes.BuiltExecutionPayloadEnv, error) | ||
| } | ||
|
|
||
| // SyncChecker exposes the node's sync status for health checks. | ||
| type SyncChecker interface { | ||
| // IsAppReady returns nil if the node has committed at least one block. | ||
| IsAppReady() error | ||
| // GetSyncData returns the latest committed height and the target height being synced to. | ||
| GetSyncData() (latestHeight int64, syncToHeight int64) | ||
| } | ||
|
|
||
| // ELChecker exposes the execution-layer client's connectivity status. | ||
| type ELChecker interface { | ||
| // IsConnected returns true if the execution client is reachable. | ||
| IsConnected() bool | ||
| } | ||
|
|
||
| // Server is the preconf API server that serves GetPayload requests from validators. | ||
| type Server struct { | ||
| logger log.Logger | ||
| validatorJWTs ValidatorJWTs | ||
| whitelist Whitelist | ||
| preconfProposerTracker ProposerTracker | ||
| payloadProvider PayloadProvider | ||
| syncChecker SyncChecker | ||
| elChecker ELChecker | ||
| port int | ||
|
|
||
| mu sync.RWMutex | ||
|
|
@@ -79,6 +95,8 @@ func NewServer( | |
| whitelist Whitelist, | ||
| preconfProposerTracker ProposerTracker, | ||
| payloadProvider PayloadProvider, | ||
| syncChecker SyncChecker, | ||
| elChecker ELChecker, | ||
| port int, | ||
| ) *Server { | ||
| return &Server{ | ||
|
|
@@ -87,6 +105,8 @@ func NewServer( | |
| whitelist: whitelist, | ||
| preconfProposerTracker: preconfProposerTracker, | ||
| payloadProvider: payloadProvider, | ||
| syncChecker: syncChecker, | ||
| elChecker: elChecker, | ||
| port: port, | ||
| } | ||
| } | ||
|
|
@@ -157,13 +177,42 @@ func (s *Server) Stop() error { | |
| return server.Shutdown(ctx) | ||
| } | ||
|
|
||
| // handleHealth just sends 200 OK to the health check endpoint. | ||
| // handleHealth checks sync status and returns 200 when the sequencer is synced | ||
| // and ready to produce blocks, or 503 when it is not. | ||
| func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { | ||
| if r.Method != http.MethodGet { | ||
| s.writeError(w, http.StatusMethodNotAllowed, "method not allowed") | ||
| return | ||
| } | ||
| w.WriteHeader(http.StatusOK) | ||
|
|
||
| resp := s.buildHealthResponse() | ||
|
|
||
| w.Header().Set("Content-Type", "application/json") | ||
| healthy := resp.IsReady && !resp.IsSyncing && resp.ELConnected | ||
| if !healthy { | ||
| w.WriteHeader(http.StatusServiceUnavailable) | ||
| } | ||
| if err := json.NewEncoder(w).Encode(resp); err != nil { | ||
| s.logger.Error("Failed to encode health response", "error", err) | ||
| } | ||
| } | ||
|
|
||
| // buildHealthResponse inspects the node's sync state and EL connectivity | ||
| // and produces a HealthResponse. | ||
| func (s *Server) buildHealthResponse() *HealthResponse { | ||
| resp := new(HealthResponse) | ||
|
|
||
| if s.syncChecker != nil { | ||
| resp.IsReady = s.syncChecker.IsAppReady() == nil | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IsAppReady should return a boolean (in general any func thats named
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agree it's a bit weird but this is just the |
||
| latestHeight, syncToHeight := s.syncChecker.GetSyncData() | ||
| resp.IsSyncing = syncToHeight > latestHeight | ||
| } | ||
|
|
||
| if s.elChecker != nil { | ||
| resp.ELConnected = s.elChecker.IsConnected() | ||
| } | ||
|
bar-bera marked this conversation as resolved.
|
||
|
|
||
| return resp | ||
| } | ||
|
|
||
| // handleGetPayload handles the GetPayload endpoint. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: to make it clear from reading this code, we can add a quick comment here explaining: