You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							208 lines
						
					
					
						
							5.3 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							208 lines
						
					
					
						
							5.3 KiB
						
					
					
				| package erasure_coding | |
| 
 | |
| import ( | |
| 	"fmt" | |
| 	"io" | |
| 	"os" | |
| 
 | |
| 	"github.com/seaweedfs/seaweedfs/weed/storage/backend" | |
| 	"github.com/seaweedfs/seaweedfs/weed/storage/idx" | |
| 	"github.com/seaweedfs/seaweedfs/weed/storage/needle" | |
| 	"github.com/seaweedfs/seaweedfs/weed/storage/needle_map" | |
| 	"github.com/seaweedfs/seaweedfs/weed/storage/super_block" | |
| 	"github.com/seaweedfs/seaweedfs/weed/storage/types" | |
| 	"github.com/seaweedfs/seaweedfs/weed/util" | |
| ) | |
| 
 | |
| // write .idx file from .ecx and .ecj files | |
| func WriteIdxFileFromEcIndex(baseFileName string) (err error) { | |
| 
 | |
| 	ecxFile, openErr := os.OpenFile(baseFileName+".ecx", os.O_RDONLY, 0644) | |
| 	if openErr != nil { | |
| 		return fmt.Errorf("cannot open ec index %s.ecx: %v", baseFileName, openErr) | |
| 	} | |
| 	defer ecxFile.Close() | |
| 
 | |
| 	idxFile, openErr := os.OpenFile(baseFileName+".idx", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) | |
| 	if openErr != nil { | |
| 		return fmt.Errorf("cannot open %s.idx: %v", baseFileName, openErr) | |
| 	} | |
| 	defer idxFile.Close() | |
| 
 | |
| 	io.Copy(idxFile, ecxFile) | |
| 
 | |
| 	err = iterateEcjFile(baseFileName, func(key types.NeedleId) error { | |
| 
 | |
| 		bytes := needle_map.ToBytes(key, types.Offset{}, types.TombstoneFileSize) | |
| 		idxFile.Write(bytes) | |
| 
 | |
| 		return nil | |
| 	}) | |
| 
 | |
| 	return err | |
| } | |
| 
 | |
| // FindDatFileSize calculate .dat file size from max offset entry | |
| // there may be extra deletions after that entry | |
| // but they are deletions anyway | |
| func FindDatFileSize(dataBaseFileName, indexBaseFileName string) (datSize int64, err error) { | |
| 
 | |
| 	version, err := readEcVolumeVersion(dataBaseFileName) | |
| 	if err != nil { | |
| 		return 0, fmt.Errorf("read ec volume %s version: %v", dataBaseFileName, err) | |
| 	} | |
| 
 | |
| 	err = iterateEcxFile(indexBaseFileName, func(key types.NeedleId, offset types.Offset, size types.Size) error { | |
| 
 | |
| 		if size.IsDeleted() { | |
| 			return nil | |
| 		} | |
| 
 | |
| 		entryStopOffset := offset.ToActualOffset() + needle.GetActualSize(size, version) | |
| 		if datSize < entryStopOffset { | |
| 			datSize = entryStopOffset | |
| 		} | |
| 
 | |
| 		return nil | |
| 	}) | |
| 
 | |
| 	return | |
| } | |
| 
 | |
| func readEcVolumeVersion(baseFileName string) (version needle.Version, err error) { | |
| 
 | |
| 	// find volume version | |
| 	datFile, err := os.OpenFile(baseFileName+".ec00", os.O_RDONLY, 0644) | |
| 	if err != nil { | |
| 		return 0, fmt.Errorf("open ec volume %s superblock: %v", baseFileName, err) | |
| 	} | |
| 	datBackend := backend.NewDiskFile(datFile) | |
| 
 | |
| 	superBlock, err := super_block.ReadSuperBlock(datBackend) | |
| 	datBackend.Close() | |
| 	if err != nil { | |
| 		return 0, fmt.Errorf("read ec volume %s superblock: %v", baseFileName, err) | |
| 	} | |
| 
 | |
| 	return superBlock.Version, nil | |
| 
 | |
| } | |
| 
 | |
| func iterateEcxFile(baseFileName string, processNeedleFn func(key types.NeedleId, offset types.Offset, size types.Size) error) error { | |
| 	ecxFile, openErr := os.OpenFile(baseFileName+".ecx", os.O_RDONLY, 0644) | |
| 	if openErr != nil { | |
| 		return fmt.Errorf("cannot open ec index %s.ecx: %v", baseFileName, openErr) | |
| 	} | |
| 	defer ecxFile.Close() | |
| 
 | |
| 	buf := make([]byte, types.NeedleMapEntrySize) | |
| 	for { | |
| 		n, err := ecxFile.Read(buf) | |
| 		if n != types.NeedleMapEntrySize { | |
| 			if err == io.EOF { | |
| 				return nil | |
| 			} | |
| 			return err | |
| 		} | |
| 		key, offset, size := idx.IdxFileEntry(buf) | |
| 		if processNeedleFn != nil { | |
| 			err = processNeedleFn(key, offset, size) | |
| 		} | |
| 		if err != nil { | |
| 			if err != io.EOF { | |
| 				return err | |
| 			} | |
| 			return nil | |
| 		} | |
| 	} | |
| 
 | |
| } | |
| 
 | |
| func iterateEcjFile(baseFileName string, processNeedleFn func(key types.NeedleId) error) error { | |
| 	if !util.FileExists(baseFileName + ".ecj") { | |
| 		return nil | |
| 	} | |
| 	ecjFile, openErr := os.OpenFile(baseFileName+".ecj", os.O_RDONLY, 0644) | |
| 	if openErr != nil { | |
| 		return fmt.Errorf("cannot open ec index %s.ecj: %v", baseFileName, openErr) | |
| 	} | |
| 	defer ecjFile.Close() | |
| 
 | |
| 	buf := make([]byte, types.NeedleIdSize) | |
| 	for { | |
| 		n, err := ecjFile.Read(buf) | |
| 		if n != types.NeedleIdSize { | |
| 			if err == io.EOF { | |
| 				return nil | |
| 			} | |
| 			return err | |
| 		} | |
| 		if processNeedleFn != nil { | |
| 			err = processNeedleFn(types.BytesToNeedleId(buf)) | |
| 		} | |
| 		if err != nil { | |
| 			if err == io.EOF { | |
| 				return nil | |
| 			} | |
| 			return err | |
| 		} | |
| 	} | |
| 
 | |
| } | |
| 
 | |
| // WriteDatFile generates .dat from from .ec00 ~ .ec09 files | |
| func WriteDatFile(baseFileName string, datFileSize int64, shardFileNames []string) error { | |
| 
 | |
| 	datFile, openErr := os.OpenFile(baseFileName+".dat", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) | |
| 	if openErr != nil { | |
| 		return fmt.Errorf("cannot write volume %s.dat: %v", baseFileName, openErr) | |
| 	} | |
| 	defer datFile.Close() | |
| 
 | |
| 	inputFiles := make([]*os.File, DataShardsCount) | |
| 
 | |
| 	defer func() { | |
| 		for shardId := 0; shardId < DataShardsCount; shardId++ { | |
| 			if inputFiles[shardId] != nil { | |
| 				inputFiles[shardId].Close() | |
| 			} | |
| 		} | |
| 	}() | |
| 
 | |
| 	for shardId := 0; shardId < DataShardsCount; shardId++ { | |
| 		inputFiles[shardId], openErr = os.OpenFile(shardFileNames[shardId], os.O_RDONLY, 0) | |
| 		if openErr != nil { | |
| 			return openErr | |
| 		} | |
| 	} | |
| 
 | |
| 	for datFileSize >= DataShardsCount*ErasureCodingLargeBlockSize { | |
| 		for shardId := 0; shardId < DataShardsCount; shardId++ { | |
| 			w, err := io.CopyN(datFile, inputFiles[shardId], ErasureCodingLargeBlockSize) | |
| 			if w != ErasureCodingLargeBlockSize { | |
| 				return fmt.Errorf("copy %s large block %d: %v", baseFileName, shardId, err) | |
| 			} | |
| 			datFileSize -= ErasureCodingLargeBlockSize | |
| 		} | |
| 	} | |
| 
 | |
| 	for datFileSize > 0 { | |
| 		for shardId := 0; shardId < DataShardsCount; shardId++ { | |
| 			toRead := min(datFileSize, ErasureCodingSmallBlockSize) | |
| 			w, err := io.CopyN(datFile, inputFiles[shardId], toRead) | |
| 			if w != toRead { | |
| 				return fmt.Errorf("copy %s small block %d: %v", baseFileName, shardId, err) | |
| 			} | |
| 			datFileSize -= toRead | |
| 		} | |
| 	} | |
| 
 | |
| 	return nil | |
| } | |
| 
 | |
| func min(x, y int64) int64 { | |
| 	if x > y { | |
| 		return y | |
| 	} | |
| 	return x | |
| }
 |