Skip to content

Commit e23f9b8

Browse files
authored
fix: non aggregator readiness (#3376)
1 parent 9e400f9 commit e23f9b8

2 files changed

Lines changed: 81 additions & 2 deletions

File tree

pkg/rpc/server/http.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ func RegisterCustomHTTPEndpoints(mux *http.ServeMux, s store.Store, pm p2p.P2PRP
7878
// 2. Block production/sync status:
7979
// - Confirms node state is accessible
8080
// - Verifies at least one block has been produced/synced
81-
// 3. Aggregator-specific checks (for aggregator nodes only):
82-
// - Validates blocks are being produced at expected rate (within 5x block_time)
81+
// 3. Block execution liveness:
82+
// - Aggregator nodes: validates blocks are being produced at expected rate (within 5x block_time)
83+
// - Non-aggregator nodes: validates a block has been executed within readiness_window_seconds
8384
// 4. Sync status (for all nodes):
8485
// - Compares local height with best known network height
8586
// - Ensures node is not falling behind by more than readiness_max_blocks_behind
@@ -134,6 +135,16 @@ func RegisterCustomHTTPEndpoints(mux *http.ServeMux, s store.Store, pm p2p.P2PRP
134135
http.Error(w, "UNREADY: aggregator not producing blocks at expected rate", http.StatusServiceUnavailable)
135136
return
136137
}
138+
} else {
139+
readinessWindowSeconds := cfg.Node.ReadinessWindowSeconds
140+
if readinessWindowSeconds == 0 {
141+
readinessWindowSeconds = 15 // fallback to default 15s window
142+
}
143+
maxAllowedDelay := time.Duration(readinessWindowSeconds) * time.Second
144+
if time.Since(state.LastBlockTime) > maxAllowedDelay {
145+
http.Error(w, "UNREADY: node not executing blocks", http.StatusServiceUnavailable)
146+
return
147+
}
137148
}
138149

139150
if bestKnownHeightProvider == nil {

pkg/rpc/server/http_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,71 @@ func TestHealthReady_aggregatorBlockDelay(t *testing.T) {
232232
})
233233
}
234234
}
235+
236+
func TestHealthReady_nonAggregatorBlockDelay(t *testing.T) {
237+
logger := zerolog.Nop()
238+
239+
type spec struct {
240+
readinessWindowSeconds uint64
241+
delay time.Duration
242+
expStatusCode int
243+
expBody string
244+
}
245+
246+
specs := map[string]spec{
247+
"within readiness window": {
248+
readinessWindowSeconds: 10,
249+
delay: 5 * time.Second,
250+
expStatusCode: http.StatusOK,
251+
expBody: "READY\n",
252+
},
253+
"exceeds readiness window": {
254+
readinessWindowSeconds: 10,
255+
delay: 15 * time.Second,
256+
expStatusCode: http.StatusServiceUnavailable,
257+
expBody: "UNREADY: node not executing blocks\n",
258+
},
259+
"zero readiness window falls back to default 15s": {
260+
readinessWindowSeconds: 0,
261+
delay: 10 * time.Second,
262+
expStatusCode: http.StatusOK,
263+
expBody: "READY\n",
264+
},
265+
}
266+
267+
for name, tc := range specs {
268+
t.Run(name, func(t *testing.T) {
269+
mux := http.NewServeMux()
270+
271+
cfg := config.DefaultConfig()
272+
cfg.Node.Aggregator = false
273+
cfg.Node.ReadinessWindowSeconds = tc.readinessWindowSeconds
274+
275+
mockStore := mocks.NewMockStore(t)
276+
state := types.State{
277+
LastBlockHeight: 10,
278+
LastBlockTime: time.Now().Add(-tc.delay),
279+
}
280+
mockStore.On("GetState", mock.Anything).Return(state, nil)
281+
282+
bestKnownHeightProvider := func() uint64 { return state.LastBlockHeight }
283+
284+
RegisterCustomHTTPEndpoints(mux, mockStore, nil, cfg, bestKnownHeightProvider, logger, nil)
285+
286+
ts := httptest.NewServer(mux)
287+
t.Cleanup(ts.Close)
288+
289+
req, err := http.NewRequest(http.MethodGet, ts.URL+"/health/ready", nil)
290+
require.NoError(t, err)
291+
resp, err := http.DefaultClient.Do(req) //nolint:gosec // ok to use default client in tests
292+
require.NoError(t, err)
293+
t.Cleanup(func() { _ = resp.Body.Close() })
294+
295+
body, err := io.ReadAll(resp.Body)
296+
require.NoError(t, err)
297+
298+
assert.Equal(t, tc.expStatusCode, resp.StatusCode)
299+
assert.Equal(t, tc.expBody, string(body))
300+
})
301+
}
302+
}

0 commit comments

Comments
 (0)