From d7d19ea9ba94ad368e58a2d1abc29a0be118bda2 Mon Sep 17 00:00:00 2001 From: chrislu Date: Wed, 13 Aug 2025 01:11:37 -0700 Subject: [PATCH] ec volume deletion is generation-aware --- weed/pb/volume_server.proto | 1 + weed/pb/volume_server_pb/volume_server.pb.go | 15 +++++++++++++-- weed/server/volume_grpc_erasure_coding.go | 10 ++++++++-- weed/worker/tasks/ec_vacuum/ec_vacuum_task.go | 17 +++-------------- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/weed/pb/volume_server.proto b/weed/pb/volume_server.proto index b6dba5f28..a45a5be75 100644 --- a/weed/pb/volume_server.proto +++ b/weed/pb/volume_server.proto @@ -417,6 +417,7 @@ message VolumeEcShardsDeleteRequest { uint32 volume_id = 1; string collection = 2; repeated uint32 shard_ids = 3; + uint32 generation = 4; // Generation support for EC vacuum cleanup } 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 4b8cc3d78..c58d8264a 100644 --- a/weed/pb/volume_server_pb/volume_server.pb.go +++ b/weed/pb/volume_server_pb/volume_server.pb.go @@ -3285,6 +3285,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"` + Generation uint32 `protobuf:"varint,4,opt,name=generation,proto3" json:"generation,omitempty"` // Generation support for EC vacuum cleanup unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3340,6 +3341,13 @@ func (x *VolumeEcShardsDeleteRequest) GetShardIds() []uint32 { return nil } +func (x *VolumeEcShardsDeleteRequest) GetGeneration() uint32 { + if x != nil { + return x.Generation + } + return 0 +} + type VolumeEcShardsDeleteResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -6538,13 +6546,16 @@ const file_volume_server_proto_rawDesc = "" + "\n" + "generation\x18\t \x01(\rR\n" + "generation\"\x1c\n" + - "\x1aVolumeEcShardsCopyResponse\"w\n" + + "\x1aVolumeEcShardsCopyResponse\"\x97\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\x1e\n" + + "\n" + + "generation\x18\x04 \x01(\rR\n" + + "generation\"\x1e\n" + "\x1cVolumeEcShardsDeleteResponse\"\x96\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 1fed1b5ce..8500a2e80 100644 --- a/weed/server/volume_grpc_erasure_coding.go +++ b/weed/server/volume_grpc_erasure_coding.go @@ -258,9 +258,15 @@ func (vs *VolumeServer) VolumeEcShardsCopy(ctx context.Context, req *volume_serv // the shard should not be mounted before calling this. func (vs *VolumeServer) VolumeEcShardsDelete(ctx context.Context, req *volume_server_pb.VolumeEcShardsDeleteRequest) (*volume_server_pb.VolumeEcShardsDeleteResponse, error) { - bName := erasure_coding.EcShardBaseFileName(req.Collection, int(req.VolumeId)) + // Use generation-aware base filename if generation is specified + var bName string + if req.Generation > 0 { + bName = erasure_coding.EcShardBaseFileNameWithGeneration(req.Collection, int(req.VolumeId), req.Generation) + } else { + bName = erasure_coding.EcShardBaseFileName(req.Collection, int(req.VolumeId)) + } - glog.V(0).Infof("ec volume %s shard delete %v", bName, req.ShardIds) + glog.V(0).Infof("ec volume %s shard delete %v generation %d", bName, req.ShardIds, req.Generation) for _, location := range vs.store.Locations { if err := deleteEcShardIdsForEachLocation(bName, location, req.ShardIds); err != nil { diff --git a/weed/worker/tasks/ec_vacuum/ec_vacuum_task.go b/weed/worker/tasks/ec_vacuum/ec_vacuum_task.go index f4330a6aa..6691b3990 100644 --- a/weed/worker/tasks/ec_vacuum/ec_vacuum_task.go +++ b/weed/worker/tasks/ec_vacuum/ec_vacuum_task.go @@ -1110,29 +1110,18 @@ func (t *EcVacuumTask) deleteGenerationFilesFromNode(client volume_server_pb.Vol VolumeId: t.volumeID, Collection: t.collection, ShardIds: allShardIds, + Generation: generation, // Pass generation for proper file cleanup }) if err != nil { // Log warning but don't fail - the unmount should have made files safe for cleanup - t.LogWarning("VolumeEcShardsDelete returned error - this is expected for generation > 0", map[string]interface{}{ + t.LogWarning("VolumeEcShardsDelete returned error", map[string]interface{}{ "volume_id": t.volumeID, "generation": generation, "error": err.Error(), - "note": "Generation > 0 files need manual cleanup or volume server extension", + "note": "File deletion failed but files were unmounted", }) - // For generation > 0, the files are unmounted but not deleted - // This is a known limitation - the volume server would need to be extended - // to support generation-aware file deletion in VolumeEcShardsDelete - if generation > 0 { - t.LogInfo("Generation > 0 file cleanup limitation", map[string]interface{}{ - "volume_id": t.volumeID, - "generation": generation, - "status": "unmounted_but_not_deleted", - "note": "Files are unmounted from memory but remain on disk until manual cleanup", - }) - } - // Don't return error - unmounting is the primary safety requirement return nil }