diff --git a/weed/pb/volume_server.proto b/weed/pb/volume_server.proto index 7c2951676..74394dbdc 100644 --- a/weed/pb/volume_server.proto +++ b/weed/pb/volume_server.proto @@ -452,6 +452,7 @@ message VolumeEcShardsDeleteRequest { uint32 volume_id = 1; string collection = 2; repeated uint32 shard_ids = 3; + bool full_teardown = 4; // pre-encode cleanup: wipe every EC artifact + generation for this volume, not just shard_ids } message VolumeEcShardsDeleteResponse { } diff --git a/weed/pb/volume_server_pb/volume_server.pb.go b/weed/pb/volume_server_pb/volume_server.pb.go index d6b25290a..0ee561708 100644 --- a/weed/pb/volume_server_pb/volume_server.pb.go +++ b/weed/pb/volume_server_pb/volume_server.pb.go @@ -3605,6 +3605,7 @@ type VolumeEcShardsDeleteRequest struct { VolumeId uint32 `protobuf:"varint,1,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"` Collection string `protobuf:"bytes,2,opt,name=collection,proto3" json:"collection,omitempty"` ShardIds []uint32 `protobuf:"varint,3,rep,packed,name=shard_ids,json=shardIds,proto3" json:"shard_ids,omitempty"` + FullTeardown bool `protobuf:"varint,4,opt,name=full_teardown,json=fullTeardown,proto3" json:"full_teardown,omitempty"` // pre-encode cleanup: wipe every EC artifact + generation for this volume, not just shard_ids unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3660,6 +3661,13 @@ func (x *VolumeEcShardsDeleteRequest) GetShardIds() []uint32 { return nil } +func (x *VolumeEcShardsDeleteRequest) GetFullTeardown() bool { + if x != nil { + return x.FullTeardown + } + return false +} + type VolumeEcShardsDeleteResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -7244,13 +7252,14 @@ const file_volume_server_proto_rawDesc = "" + "\rcopy_vif_file\x18\a \x01(\bR\vcopyVifFile\x12\x17\n" + "\adisk_id\x18\b \x01(\rR\x06diskId\x12&\n" + "\x0fcopy_ecsum_file\x18\t \x01(\bR\rcopyEcsumFile\"\x1c\n" + - "\x1aVolumeEcShardsCopyResponse\"w\n" + + "\x1aVolumeEcShardsCopyResponse\"\x9c\x01\n" + "\x1bVolumeEcShardsDeleteRequest\x12\x1b\n" + "\tvolume_id\x18\x01 \x01(\rR\bvolumeId\x12\x1e\n" + "\n" + "collection\x18\x02 \x01(\tR\n" + "collection\x12\x1b\n" + - "\tshard_ids\x18\x03 \x03(\rR\bshardIds\"\x1e\n" + + "\tshard_ids\x18\x03 \x03(\rR\bshardIds\x12#\n" + + "\rfull_teardown\x18\x04 \x01(\bR\ffullTeardown\"\x1e\n" + "\x1cVolumeEcShardsDeleteResponse\"\xa0\x01\n" + "\x1aVolumeEcShardsMountRequest\x12\x1b\n" + "\tvolume_id\x18\x01 \x01(\rR\bvolumeId\x12\x1e\n" + diff --git a/weed/server/volume_grpc_erasure_coding.go b/weed/server/volume_grpc_erasure_coding.go index b9045d1e5..126e41729 100644 --- a/weed/server/volume_grpc_erasure_coding.go +++ b/weed/server/volume_grpc_erasure_coding.go @@ -417,6 +417,23 @@ func (vs *VolumeServer) VolumeEcShardsDelete(ctx context.Context, req *volume_se bName := erasure_coding.EcShardBaseFileName(req.Collection, int(req.VolumeId)) + if req.FullTeardown { + // Pre-encode cleanup: evict the volume and wipe every EC artifact for it on + // every disk (the same teardown the generator does locally), not just the + // listed shards, so a remote node retains no stale generation that a fresh + // gen-0 copy would later collide with. + glog.V(0).Infof("ec volume %s full teardown", bName) + vs.store.UnloadEcVolume(needle.VolumeId(req.VolumeId)) + for _, location := range vs.store.Locations { + dataBase := storage.VolumeFileName(location.Directory, req.Collection, int(req.VolumeId)) + idxBase := storage.VolumeFileName(location.IdxDirectory, req.Collection, int(req.VolumeId)) + if err := removeStaleEcArtifacts(dataBase, idxBase, erasure_coding.MaxShardCount); err != nil { + return nil, fmt.Errorf("full teardown of ec volume %d on %s: %w", req.VolumeId, location.Directory, err) + } + } + return &volume_server_pb.VolumeEcShardsDeleteResponse{}, nil + } + glog.V(0).Infof("ec volume %s shard delete %v", bName, req.ShardIds) for diskId, location := range vs.store.Locations { diff --git a/weed/shell/command_ec_common.go b/weed/shell/command_ec_common.go index 8a7830adb..7afc21cd6 100644 --- a/weed/shell/command_ec_common.go +++ b/weed/shell/command_ec_common.go @@ -550,9 +550,10 @@ func unmountAndDeleteEcShardsQuiet(grpcDialOption grpc.DialOption, collection st return fmt.Errorf("unmount: %w", err) } if _, err := volumeServerClient.VolumeEcShardsDelete(context.Background(), &volume_server_pb.VolumeEcShardsDeleteRequest{ - VolumeId: uint32(volumeId), - Collection: collection, - ShardIds: ids, + VolumeId: uint32(volumeId), + Collection: collection, + ShardIds: ids, + FullTeardown: true, }); err != nil { return fmt.Errorf("delete: %w", err) } diff --git a/weed/worker/tasks/erasure_coding/ec_task.go b/weed/worker/tasks/erasure_coding/ec_task.go index 717a594f0..fcc5921f5 100644 --- a/weed/worker/tasks/erasure_coding/ec_task.go +++ b/weed/worker/tasks/erasure_coding/ec_task.go @@ -978,9 +978,10 @@ func unmountAndDeleteEcShards( return fmt.Errorf("unmount: %w", err) } if _, err := client.VolumeEcShardsDelete(ctx, &volume_server_pb.VolumeEcShardsDeleteRequest{ - VolumeId: volumeID, - Collection: collection, - ShardIds: shardIds, + VolumeId: volumeID, + Collection: collection, + ShardIds: shardIds, + FullTeardown: true, }); err != nil { return fmt.Errorf("delete: %w", err) }