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.

202 lines
5.2 KiB

  1. package erasure_coding
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "github.com/chrislusf/seaweedfs/weed/storage/backend"
  7. "github.com/chrislusf/seaweedfs/weed/storage/idx"
  8. "github.com/chrislusf/seaweedfs/weed/storage/needle"
  9. "github.com/chrislusf/seaweedfs/weed/storage/needle_map"
  10. "github.com/chrislusf/seaweedfs/weed/storage/super_block"
  11. "github.com/chrislusf/seaweedfs/weed/storage/types"
  12. "github.com/chrislusf/seaweedfs/weed/util"
  13. )
  14. // write .idx file from .ecx and .ecj files
  15. func WriteIdxFileFromEcIndex(baseFileName string) (err error) {
  16. ecxFile, openErr := os.OpenFile(baseFileName+".ecx", os.O_RDONLY, 0644)
  17. if openErr != nil {
  18. return fmt.Errorf("cannot open ec index %s.ecx: %v", baseFileName, openErr)
  19. }
  20. defer ecxFile.Close()
  21. idxFile, openErr := os.OpenFile(baseFileName+".idx", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
  22. if openErr != nil {
  23. return fmt.Errorf("cannot open %s.idx: %v", baseFileName, openErr)
  24. }
  25. defer idxFile.Close()
  26. io.Copy(idxFile, ecxFile)
  27. err = iterateEcjFile(baseFileName, func(key types.NeedleId) error {
  28. bytes := needle_map.ToBytes(key, types.Offset{}, types.TombstoneFileSize)
  29. idxFile.Write(bytes)
  30. return nil
  31. })
  32. return err
  33. }
  34. // FindDatFileSize calculate .dat file size from max offset entry
  35. // there may be extra deletions after that entry
  36. // but they are deletions anyway
  37. func FindDatFileSize(baseFileName string) (datSize int64, err error) {
  38. version, err := readEcVolumeVersion(baseFileName)
  39. if err != nil {
  40. return 0, fmt.Errorf("read ec volume %s version: %v", baseFileName, err)
  41. }
  42. err = iterateEcxFile(baseFileName, func(key types.NeedleId, offset types.Offset, size uint32) error {
  43. if size == types.TombstoneFileSize {
  44. return nil
  45. }
  46. entryStopOffset := offset.ToAcutalOffset() + needle.GetActualSize(size, version)
  47. if datSize < entryStopOffset {
  48. datSize = entryStopOffset
  49. }
  50. return nil
  51. })
  52. return
  53. }
  54. func readEcVolumeVersion(baseFileName string) (version needle.Version, err error) {
  55. // find volume version
  56. datFile, err := os.OpenFile(baseFileName+".ec00", os.O_RDONLY, 0644)
  57. if err != nil {
  58. return 0, fmt.Errorf("open ec volume %s superblock: %v", baseFileName, err)
  59. }
  60. datBackend := backend.NewDiskFile(datFile)
  61. superBlock, err := super_block.ReadSuperBlock(datBackend)
  62. datBackend.Close()
  63. if err != nil {
  64. return 0, fmt.Errorf("read ec volume %s superblock: %v", baseFileName, err)
  65. }
  66. return superBlock.Version, nil
  67. }
  68. func iterateEcxFile(baseFileName string, processNeedleFn func(key types.NeedleId, offset types.Offset, size uint32) error) error {
  69. ecxFile, openErr := os.OpenFile(baseFileName+".ecx", os.O_RDONLY, 0644)
  70. if openErr != nil {
  71. return fmt.Errorf("cannot open ec index %s.ecx: %v", baseFileName, openErr)
  72. }
  73. defer ecxFile.Close()
  74. buf := make([]byte, types.NeedleMapEntrySize)
  75. for {
  76. n, err := ecxFile.Read(buf)
  77. if n != types.NeedleMapEntrySize {
  78. if err == io.EOF {
  79. return nil
  80. }
  81. return err
  82. }
  83. key, offset, size := idx.IdxFileEntry(buf)
  84. if processNeedleFn != nil {
  85. err = processNeedleFn(key, offset, size)
  86. }
  87. if err != nil {
  88. if err != io.EOF {
  89. return err
  90. }
  91. return nil
  92. }
  93. }
  94. }
  95. func iterateEcjFile(baseFileName string, processNeedleFn func(key types.NeedleId) error) error {
  96. if !util.FileExists(baseFileName+".ecj") {
  97. return nil
  98. }
  99. ecjFile, openErr := os.OpenFile(baseFileName+".ecj", os.O_RDONLY, 0644)
  100. if openErr != nil {
  101. return fmt.Errorf("cannot open ec index %s.ecj: %v", baseFileName, openErr)
  102. }
  103. defer ecjFile.Close()
  104. buf := make([]byte, types.NeedleIdSize)
  105. for {
  106. n, err := ecjFile.Read(buf)
  107. if n != types.NeedleIdSize {
  108. if err == io.EOF {
  109. return nil
  110. }
  111. return err
  112. }
  113. if processNeedleFn != nil {
  114. err = processNeedleFn(types.BytesToNeedleId(buf))
  115. }
  116. if err != nil {
  117. if err == io.EOF {
  118. return nil
  119. }
  120. return err
  121. }
  122. }
  123. }
  124. // WriteDatFile generates .dat from from .ec00 ~ .ec09 files
  125. func WriteDatFile(baseFileName string, datFileSize int64) error {
  126. datFile, openErr := os.OpenFile(baseFileName+".dat", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
  127. if openErr != nil {
  128. return fmt.Errorf("cannot write volume %s.dat: %v", baseFileName, openErr)
  129. }
  130. defer datFile.Close()
  131. inputFiles := make([]*os.File, DataShardsCount)
  132. for shardId := 0; shardId < DataShardsCount; shardId++ {
  133. shardFileName := baseFileName + ToExt(shardId)
  134. inputFiles[shardId], openErr = os.OpenFile(shardFileName, os.O_RDONLY, 0)
  135. if openErr != nil {
  136. return openErr
  137. }
  138. defer inputFiles[shardId].Close()
  139. }
  140. for datFileSize >= DataShardsCount*ErasureCodingLargeBlockSize {
  141. for shardId := 0; shardId < DataShardsCount; shardId++ {
  142. w, err := io.CopyN(datFile, inputFiles[shardId], ErasureCodingLargeBlockSize)
  143. if w != ErasureCodingLargeBlockSize {
  144. return fmt.Errorf("copy %s large block %d: %v", baseFileName, shardId, err)
  145. }
  146. datFileSize -= ErasureCodingLargeBlockSize
  147. }
  148. }
  149. for datFileSize > 0 {
  150. for shardId := 0; shardId < DataShardsCount; shardId++ {
  151. toRead := min(datFileSize, ErasureCodingSmallBlockSize)
  152. w, err := io.CopyN(datFile, inputFiles[shardId], toRead)
  153. if w != toRead {
  154. return fmt.Errorf("copy %s small block %d: %v", baseFileName, shardId, err)
  155. }
  156. datFileSize -= toRead
  157. }
  158. }
  159. return nil
  160. }
  161. func min(x, y int64) int64 {
  162. if x > y {
  163. return y
  164. }
  165. return x
  166. }