fix(CubeMaster): stably sort sandbox list to stop reordering on refresh#762
Conversation
ListSandbox fans out to per-node goroutines and appends results in completion order, so the list was reshuffled on every call. Sort the aggregated result by creation time descending (newest first), with SandboxID as a deterministic tie-breaker, giving a stable order across refreshes for the WebUI and SDK. Fixes TencentCloud#742. Signed-off-by: yingjun <yingjun.yc@antgroup.com>
| // Results arrive over resChan in goroutine-completion order, which varies | ||
| // between calls and across nodes, so rsp.Data would otherwise be reshuffled | ||
| // on every list request. Sort by creation time descending (newest first), | ||
| // falling back to SandboxID for a deterministic, stable order. |
There was a problem hiding this comment.
Suggestion: "stable" has a specific meaning in Go's sort package — sort.Slice is explicitly not a stable sort (see Go docs: "The sort is not guaranteed to be stable"). Since the comparator defines a total order via the SandboxID tiebreaker, the result is deterministic regardless of algorithm stability. Consider rewording to:
| // falling back to SandboxID for a deterministic, stable order. | |
| // falling back to SandboxID for a deterministic order. |
This avoids confusion with sort.SliceStable semantics.
| // between calls and across nodes, so rsp.Data would otherwise be reshuffled | ||
| // on every list request. Sort by creation time descending (newest first), | ||
| // falling back to SandboxID for a deterministic, stable order. | ||
| sort.Slice(rsp.Data, func(i, j int) bool { |
There was a problem hiding this comment.
Suggestion: Consider extracting this sort closure into a named function (sortSandboxBriefData) for testability. Currently it can only be tested via the full ListSandbox function (requiring mocked cubelet RPCs and goroutine orchestration). A package-level function would allow direct table-driven unit tests covering: distinct timestamps, equal timestamps (tiebreaker order), single element, and empty slice — without the integration overhead.
Review: PR #762 — fix(CubeMaster): stably sort sandbox list to stop reordering on refreshFiles changed: 1 file, +12/-0 SummaryThe PR correctly addresses the root cause of the sandbox list reordering on every refresh: ListSandbox fan-outs to per-node goroutines, and results are appended in goroutine-completion order. Adding a deterministic sort by CreateAt descending (with SandboxID tiebreaker) after aggregation is the right fix at the right layer. Positive findings
Noteworthy feedback
Inline commentsTwo inline comments have been posted on the diff corresponding to feedback items 1 and 2 above.
|
Fixes #742.
Problem
The Sandboxes list (
:12088/sandboxes) reorders on every ~auto-refresh.ListSandboxfans out to per-node goroutines and appends results torsp.Datain goroutine-completion order, which varies between calls, so the WebUI/SDK receive a reshuffled list each poll. No layer (CubeMaster, CubeAPI, frontend) imposed a stable order.Root cause
The request path is:
WebUI → CubeAPI
GET /v2/sandboxes→ CubeMasterListSandbox(gRPC) → per-node Cubelet.In
CubeMaster/pkg/service/sandbox/sandbox_list.go,ListSandboxfans out to one goroutine per node and aggregates the results through a channel:Results are appended in goroutine-completion order, which is non-deterministic and varies between calls and across nodes. No layer (CubeMaster, CubeAPI, or the frontend) applied any sort, so each refresh returned the same set of sandboxes in a different order.
Fix
Add a stable sort after aggregation in ListSandbox, before returning:
This is consistent with the existing cubecli list command, which already sorts by CreatedAt descending. The fix is contained to the CubeMaster aggregation layer, so all consumers (WebUI and SDK) get a stable order without frontend changes.