diff --git a/weed/storage/erasure_coding/ec_shard.go b/weed/storage/erasure_coding/ec_shard.go index e55a9f676..559fd4f4a 100644 --- a/weed/storage/erasure_coding/ec_shard.go +++ b/weed/storage/erasure_coding/ec_shard.go @@ -70,21 +70,63 @@ func (shard *EcVolumeShard) FileName() (fileName string) { return EcShardFileName(shard.Collection, shard.dir, int(shard.VolumeId)) } +func (shard *EcVolumeShard) FileNameWithGeneration(generation uint32) (fileName string) { + return EcShardFileNameWithGeneration(shard.Collection, shard.dir, int(shard.VolumeId), generation) +} + func EcShardFileName(collection string, dir string, id int) (fileName string) { + return EcShardFileNameWithGeneration(collection, dir, id, 0) +} + +// EcShardFileNameWithGeneration generates filename for EC volume files with generation support +// +// Generation File Layout Design: +// +// - Generation 0 (default): Uses existing filename format for backward compatibility +// Example: "data_123" -> data_123.vif, data_123.ecx, data_123.ec00, etc. +// +// - Generation > 0: Adds "_g{N}" suffix to base filename +// Example: "data_123_g1" -> data_123_g1.vif, data_123_g1.ecx, data_123_g1.ec00, etc. +// +// This approach provides: +// - Backward compatibility: Existing volumes continue to work without changes +// - Atomic operations: All files for a generation share the same base name +// - Easy cleanup: Delete files matching pattern "volume_*_g{N}.*" +// - Performance: All files remain in the same directory for fast access +func EcShardFileNameWithGeneration(collection string, dir string, id int, generation uint32) (fileName string) { idString := strconv.Itoa(id) + var baseFileName string + if collection == "" { - fileName = path.Join(dir, idString) + baseFileName = idString } else { - fileName = path.Join(dir, collection+"_"+idString) + baseFileName = collection + "_" + idString + } + + // Add generation suffix for non-zero generations (backward compatibility) + if generation > 0 { + baseFileName = baseFileName + "_g" + strconv.FormatUint(uint64(generation), 10) } + + fileName = path.Join(dir, baseFileName) return } func EcShardBaseFileName(collection string, id int) (baseFileName string) { + return EcShardBaseFileNameWithGeneration(collection, id, 0) +} + +func EcShardBaseFileNameWithGeneration(collection string, id int, generation uint32) (baseFileName string) { baseFileName = strconv.Itoa(id) if collection != "" { baseFileName = collection + "_" + baseFileName } + + // Add generation suffix for non-zero generations (backward compatibility) + if generation > 0 { + baseFileName = baseFileName + "_g" + strconv.FormatUint(uint64(generation), 10) + } + return } diff --git a/weed/storage/erasure_coding/ec_volume.go b/weed/storage/erasure_coding/ec_volume.go index 839428e7b..c18835119 100644 --- a/weed/storage/erasure_coding/ec_volume.go +++ b/weed/storage/erasure_coding/ec_volume.go @@ -155,22 +155,34 @@ func (ev *EcVolume) Destroy() { } func (ev *EcVolume) FileName(ext string) string { + return ev.FileNameWithGeneration(ext, 0) +} + +func (ev *EcVolume) FileNameWithGeneration(ext string, generation uint32) string { switch ext { case ".ecx", ".ecj": - return ev.IndexBaseFileName() + ext + return ev.IndexBaseFileNameWithGeneration(generation) + ext } // .vif - return ev.DataBaseFileName() + ext + return ev.DataBaseFileNameWithGeneration(generation) + ext } func (ev *EcVolume) DataBaseFileName() string { return EcShardFileName(ev.Collection, ev.dir, int(ev.VolumeId)) } +func (ev *EcVolume) DataBaseFileNameWithGeneration(generation uint32) string { + return EcShardFileNameWithGeneration(ev.Collection, ev.dir, int(ev.VolumeId), generation) +} + func (ev *EcVolume) IndexBaseFileName() string { return EcShardFileName(ev.Collection, ev.dirIdx, int(ev.VolumeId)) } +func (ev *EcVolume) IndexBaseFileNameWithGeneration(generation uint32) string { + return EcShardFileNameWithGeneration(ev.Collection, ev.dirIdx, int(ev.VolumeId), generation) +} + func (ev *EcVolume) ShardSize() uint64 { if len(ev.Shards) > 0 { return uint64(ev.Shards[0].Size())