From a87a4f920c519079a7ffba5d537d24f7569ea9a9 Mon Sep 17 00:00:00 2001 From: Shreyas Patil Date: Thu, 16 Jul 2026 23:04:59 +0000 Subject: [PATCH] api(ticdc): return task_status for changefeeds in warning state The changefeed detail handlers populate task_status only when the changefeed is in the normal state, so `cdc cli changefeed query` omits the per-capture table assignment for changefeeds in the warning state -- which is often exactly when this information is most needed for debugging. A changefeed in the warning state is still running (its processors are active), so its task statuses remain meaningful. Gate the task_status population on FeedState.IsRunning() (normal OR warning) instead of an exact StateNormal check. --- cdc/api/v1/api.go | 2 +- cdc/api/v2/changefeed.go | 4 ++-- cdc/api/v2/changefeed_test.go | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/cdc/api/v1/api.go b/cdc/api/v1/api.go index 0540510c2e..9d35cd4fd5 100644 --- a/cdc/api/v1/api.go +++ b/cdc/api/v1/api.go @@ -210,7 +210,7 @@ func (h *OpenAPI) GetChangefeed(c *gin.Context) { } taskStatus := make([]model.CaptureTaskStatus, 0) - if info.State == model.StateNormal { + if info.State.IsRunning() { processorInfos, err := h.statusProvider().GetAllTaskStatuses(ctx, changefeedID) if err != nil { _ = c.Error(err) diff --git a/cdc/api/v2/changefeed.go b/cdc/api/v2/changefeed.go index 4aee9bae27..428718dc1e 100644 --- a/cdc/api/v2/changefeed.go +++ b/cdc/api/v2/changefeed.go @@ -594,7 +594,7 @@ func (h *OpenAPIV2) getChangeFeed(c *gin.Context) { } taskStatus := make([]model.CaptureTaskStatus, 0) - if cfInfo.State == model.StateNormal { + if cfInfo.State.IsRunning() { processorInfos, err := h.capture.StatusProvider().GetAllTaskStatuses( ctx, changefeedID, @@ -718,7 +718,7 @@ func (h *OpenAPIV2) getChangeFeedMetaInfo(c *gin.Context) { return } taskStatus := make([]model.CaptureTaskStatus, 0) - if info.State == model.StateNormal { + if info.State.IsRunning() { processorInfos, err := h.capture.StatusProvider().GetAllTaskStatuses( ctx, changefeedID, diff --git a/cdc/api/v2/changefeed_test.go b/cdc/api/v2/changefeed_test.go index c7755d6bee..79b4ac3dc2 100644 --- a/cdc/api/v2/changefeed_test.go +++ b/cdc/api/v2/changefeed_test.go @@ -338,6 +338,44 @@ func TestGetChangeFeed(t *testing.T) { require.Nil(t, err) require.Equal(t, resp.ID, validID) require.Nil(t, resp.Error) + + // task_status should be returned for running changefeeds, including + // those in the warning state. + statusProvider.taskStatus = map[model.CaptureID]*model.TaskStatus{ + "capture-1": {Tables: map[model.TableID]*model.TableReplicaInfo{ + 3508: {}, 3520: {}, + }}, + } + for _, tc := range []struct { + state model.FeedState + wantTaskStatus bool + }{ + {model.StateNormal, true}, + {model.StateWarning, true}, + {model.StateStopped, false}, + } { + statusProvider.changefeedInfo = &model.ChangeFeedInfo{ID: validID, State: tc.state} + w = httptest.NewRecorder() + req, _ = http.NewRequestWithContext( + context.Background(), + cfInfo.method, + fmt.Sprintf(cfInfo.url, validID, "abc"), + nil, + ) + router.ServeHTTP(w, req) + require.Equal(t, http.StatusOK, w.Code) + resp = ChangeFeedInfo{} + err = json.NewDecoder(w.Body).Decode(&resp) + require.Nil(t, err) + require.Equal(t, tc.state, resp.State) + if tc.wantTaskStatus { + require.Len(t, resp.TaskStatus, 1, "state %s should return task_status", tc.state) + require.Equal(t, "capture-1", resp.TaskStatus[0].CaptureID) + require.ElementsMatch(t, []model.TableID{3508, 3520}, resp.TaskStatus[0].Tables) + } else { + require.Empty(t, resp.TaskStatus, "state %s should not return task_status", tc.state) + } + } } func TestUpdateChangefeed(t *testing.T) {