From 0dc65e7069a2d0e6f7daec94698d6677f469a8b7 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 18 May 2026 19:43:18 -0700 Subject: [PATCH] fix(admin.plugin): include disk_id in EC execution plan (#9547) TaskSource and TaskTarget carry disk_id on the wire, but the execution plan map built for the admin UI dropped the field entirely. On a multi-disk node holding shards of the same volume, there was no way to tell from the plan which disk would receive each shard. Include disk_id on each endpoint and target_disk_id on each shard assignment, and extend the existing execution-plan test to set and assert the field. --- weed/admin/plugin/job_execution_plan.go | 5 ++++ weed/admin/plugin/plugin_monitor_test.go | 31 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/weed/admin/plugin/job_execution_plan.go b/weed/admin/plugin/job_execution_plan.go index c1503e9cb..da2fbb4fd 100644 --- a/weed/admin/plugin/job_execution_plan.go +++ b/weed/admin/plugin/job_execution_plan.go @@ -115,6 +115,7 @@ func buildErasureCodingExecutionPlan(params *worker_pb.TaskParams) map[string]in source.DataCenter, source.Rack, source.VolumeId, + source.DiskId, source.ShardIds, dataShards, )) @@ -132,6 +133,7 @@ func buildErasureCodingExecutionPlan(params *worker_pb.TaskParams) map[string]in target.DataCenter, target.Rack, target.VolumeId, + target.DiskId, target.ShardIds, dataShards, )) @@ -147,6 +149,7 @@ func buildErasureCodingExecutionPlan(params *worker_pb.TaskParams) map[string]in "target_data_center": strings.TrimSpace(target.DataCenter), "target_rack": strings.TrimSpace(target.Rack), "target_volume_id": int(target.VolumeId), + "target_disk_id": int(target.DiskId), }) } } @@ -182,6 +185,7 @@ func buildExecutionEndpoint( dataCenter string, rack string, volumeID uint32, + diskID uint32, shardIDs []uint32, dataShardCount int, ) map[string]interface{} { @@ -201,6 +205,7 @@ func buildExecutionEndpoint( "data_center": strings.TrimSpace(dataCenter), "rack": strings.TrimSpace(rack), "volume_id": int(volumeID), + "disk_id": int(diskID), "shard_ids": allShards, "data_shard_ids": dataShards, "parity_shard_ids": parityShards, diff --git a/weed/admin/plugin/plugin_monitor_test.go b/weed/admin/plugin/plugin_monitor_test.go index 09281257f..d44272bc1 100644 --- a/weed/admin/plugin/plugin_monitor_test.go +++ b/weed/admin/plugin/plugin_monitor_test.go @@ -423,6 +423,7 @@ func TestTrackExecutionStartStoresErasureCodingExecutionPlan(t *testing.T) { DataCenter: "dc1", Rack: "rack1", VolumeId: 29, + DiskId: 5, }, }, Targets: []*worker_pb.TaskTarget{ @@ -431,6 +432,7 @@ func TestTrackExecutionStartStoresErasureCodingExecutionPlan(t *testing.T) { DataCenter: "dc1", Rack: "rack2", VolumeId: 29, + DiskId: 2, ShardIds: []uint32{0, 10}, }, { @@ -438,6 +440,7 @@ func TestTrackExecutionStartStoresErasureCodingExecutionPlan(t *testing.T) { DataCenter: "dc2", Rack: "rack3", VolumeId: 29, + DiskId: 3, ShardIds: []uint32{1, 11}, }, }, @@ -486,10 +489,28 @@ func TestTrackExecutionStartStoresErasureCodingExecutionPlan(t *testing.T) { if plan["volume_id"] != float64(29) { t.Fatalf("unexpected execution plan volume id: %+v", plan["volume_id"]) } + sourcesRaw, ok := plan["sources"].([]interface{}) + if !ok || len(sourcesRaw) != 1 { + t.Fatalf("unexpected sources in execution plan: %+v", plan["sources"]) + } + firstSource, ok := sourcesRaw[0].(map[string]interface{}) + if !ok { + t.Fatalf("unexpected source payload: %+v", sourcesRaw[0]) + } + if firstSource["disk_id"] != float64(5) { + t.Fatalf("unexpected source disk_id: %+v", firstSource["disk_id"]) + } targets, ok := plan["targets"].([]interface{}) if !ok || len(targets) != 2 { t.Fatalf("unexpected targets in execution plan: %+v", plan["targets"]) } + firstTarget, ok := targets[0].(map[string]interface{}) + if !ok { + t.Fatalf("unexpected target payload: %+v", targets[0]) + } + if firstTarget["disk_id"] != float64(2) { + t.Fatalf("unexpected target disk_id: %+v", firstTarget["disk_id"]) + } assignments, ok := plan["shard_assignments"].([]interface{}) if !ok || len(assignments) != 4 { t.Fatalf("unexpected shard assignments in execution plan: %+v", plan["shard_assignments"]) @@ -501,6 +522,16 @@ func TestTrackExecutionStartStoresErasureCodingExecutionPlan(t *testing.T) { if firstAssignment["shard_id"] != float64(0) || firstAssignment["kind"] != "data" { t.Fatalf("unexpected first assignment: %+v", firstAssignment) } + if firstAssignment["target_disk_id"] != float64(2) { + t.Fatalf("unexpected first assignment target_disk_id: %+v", firstAssignment["target_disk_id"]) + } + secondAssignment, ok := assignments[1].(map[string]interface{}) + if !ok { + t.Fatalf("unexpected second assignment payload: %+v", assignments[1]) + } + if secondAssignment["shard_id"] != float64(1) || secondAssignment["target_disk_id"] != float64(3) { + t.Fatalf("unexpected second assignment: %+v", secondAssignment) + } } func TestBuildJobDetailIncludesActivitiesAndRunRecord(t *testing.T) {