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.

250 lines
7.5 KiB

6 years ago
6 years ago
  1. package weed_server
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "math"
  8. "os"
  9. "path"
  10. "strings"
  11. "github.com/chrislusf/seaweedfs/weed/glog"
  12. "github.com/chrislusf/seaweedfs/weed/operation"
  13. "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb"
  14. "github.com/chrislusf/seaweedfs/weed/storage"
  15. "github.com/chrislusf/seaweedfs/weed/storage/erasure_coding"
  16. "github.com/chrislusf/seaweedfs/weed/storage/needle"
  17. "github.com/chrislusf/seaweedfs/weed/util"
  18. )
  19. /*
  20. Steps to apply erasure coding to .dat .idx files
  21. 0. ensure the volume is readonly
  22. 1. client call VolumeEcShardsGenerate to generate the .ecx and .ec01~.ec14 files
  23. 2. client ask master for possible servers to hold the ec files, at least 4 servers
  24. 3. client call VolumeEcShardsCopy on above target servers to copy ec files from the source server
  25. 4. target servers report the new ec files to the master
  26. 5. master stores vid -> [14]*DataNode
  27. 6. client checks master. If all 14 slices are ready, delete the original .idx, .idx files
  28. */
  29. // VolumeEcShardsGenerate generates the .ecx and .ec01 ~ .ec14 files
  30. func (vs *VolumeServer) VolumeEcShardsGenerate(ctx context.Context, req *volume_server_pb.VolumeEcShardsGenerateRequest) (*volume_server_pb.VolumeEcShardsGenerateResponse, error) {
  31. v := vs.store.GetVolume(needle.VolumeId(req.VolumeId))
  32. if v == nil {
  33. return nil, fmt.Errorf("volume %d not found", req.VolumeId)
  34. }
  35. baseFileName := v.FileName()
  36. if v.Collection != req.Collection {
  37. return nil, fmt.Errorf("existing collection:%v unexpected input: %v", v.Collection, req.Collection)
  38. }
  39. // write .ecx file
  40. if err := erasure_coding.WriteSortedEcxFile(baseFileName); err != nil {
  41. return nil, fmt.Errorf("WriteSortedEcxFile %s: %v", baseFileName, err)
  42. }
  43. // write .ec01 ~ .ec14 files
  44. if err := erasure_coding.WriteEcFiles(baseFileName); err != nil {
  45. return nil, fmt.Errorf("WriteEcFiles %s: %v", baseFileName, err)
  46. }
  47. return &volume_server_pb.VolumeEcShardsGenerateResponse{}, nil
  48. }
  49. // VolumeEcShardsRebuild generates the any of the missing .ec01 ~ .ec14 files
  50. func (vs *VolumeServer) VolumeEcShardsRebuild(ctx context.Context, req *volume_server_pb.VolumeEcShardsRebuildRequest) (*volume_server_pb.VolumeEcShardsRebuildResponse, error) {
  51. baseFileName := erasure_coding.EcShardBaseFileName(req.Collection, int(req.VolumeId))
  52. var rebuiltShardIds []uint32
  53. for _, location := range vs.store.Locations {
  54. if util.FileExists(path.Join(location.Directory, baseFileName+".ecx")) {
  55. // write .ec01 ~ .ec14 files
  56. if generatedShardIds, err := erasure_coding.RebuildEcFiles(baseFileName); err != nil {
  57. return nil, fmt.Errorf("RebuildEcFiles %s: %v", baseFileName, err)
  58. } else {
  59. rebuiltShardIds = generatedShardIds
  60. }
  61. break
  62. }
  63. }
  64. return &volume_server_pb.VolumeEcShardsRebuildResponse{
  65. RebuiltShardIds: rebuiltShardIds,
  66. }, nil
  67. }
  68. // VolumeEcShardsCopy copy the .ecx and some ec data slices
  69. func (vs *VolumeServer) VolumeEcShardsCopy(ctx context.Context, req *volume_server_pb.VolumeEcShardsCopyRequest) (*volume_server_pb.VolumeEcShardsCopyResponse, error) {
  70. location := vs.store.FindFreeLocation()
  71. if location == nil {
  72. return nil, fmt.Errorf("no space left")
  73. }
  74. baseFileName := storage.VolumeFileName(location.Directory, req.Collection, int(req.VolumeId))
  75. err := operation.WithVolumeServerClient(req.SourceDataNode, vs.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error {
  76. // copy ec data slices
  77. for _, shardId := range req.ShardIds {
  78. if err := vs.doCopyFile(ctx, client, true, req.Collection, req.VolumeId, math.MaxUint32, math.MaxInt64, baseFileName, erasure_coding.ToExt(int(shardId))); err != nil {
  79. return err
  80. }
  81. }
  82. if !req.CopyEcxFile {
  83. return nil
  84. }
  85. // copy ecx file
  86. if err := vs.doCopyFile(ctx, client, true, req.Collection, req.VolumeId, math.MaxUint32, math.MaxInt64, baseFileName, ".ecx"); err != nil {
  87. return err
  88. }
  89. return nil
  90. })
  91. if err != nil {
  92. return nil, fmt.Errorf("VolumeEcShardsCopy volume %d: %v", req.VolumeId, err)
  93. }
  94. return &volume_server_pb.VolumeEcShardsCopyResponse{}, nil
  95. }
  96. // VolumeEcShardsDelete local delete the .ecx and some ec data slices if not needed
  97. // the shard should not be mounted before calling this.
  98. func (vs *VolumeServer) VolumeEcShardsDelete(ctx context.Context, req *volume_server_pb.VolumeEcShardsDeleteRequest) (*volume_server_pb.VolumeEcShardsDeleteResponse, error) {
  99. baseFilename := erasure_coding.EcShardBaseFileName(req.Collection, int(req.VolumeId))
  100. for _, shardId := range req.ShardIds {
  101. os.Remove(baseFilename + erasure_coding.ToExt(int(shardId)))
  102. }
  103. // check whether to delete the ecx file also
  104. hasEcxFile := false
  105. existingShardCount := 0
  106. for _, location := range vs.store.Locations {
  107. fileInfos, err := ioutil.ReadDir(location.Directory)
  108. if err != nil {
  109. continue
  110. }
  111. for _, fileInfo := range fileInfos {
  112. if fileInfo.Name() == baseFilename+".ecx" {
  113. hasEcxFile = true
  114. continue
  115. }
  116. if strings.HasPrefix(fileInfo.Name(), baseFilename+".ec") {
  117. existingShardCount++
  118. }
  119. }
  120. }
  121. if hasEcxFile && existingShardCount == 0 {
  122. if err := os.Remove(baseFilename + ".ecx"); err != nil {
  123. return nil, err
  124. }
  125. }
  126. return &volume_server_pb.VolumeEcShardsDeleteResponse{}, nil
  127. }
  128. func (vs *VolumeServer) VolumeEcShardsMount(ctx context.Context, req *volume_server_pb.VolumeEcShardsMountRequest) (*volume_server_pb.VolumeEcShardsMountResponse, error) {
  129. for _, shardId := range req.ShardIds {
  130. err := vs.store.MountEcShards(req.Collection, needle.VolumeId(req.VolumeId), erasure_coding.ShardId(shardId))
  131. if err != nil {
  132. glog.Errorf("ec shard mount %v: %v", req, err)
  133. } else {
  134. glog.V(2).Infof("ec shard mount %v", req)
  135. }
  136. if err != nil {
  137. return nil, fmt.Errorf("mount %d.%d: %v", req.VolumeId, shardId, err)
  138. }
  139. }
  140. return &volume_server_pb.VolumeEcShardsMountResponse{}, nil
  141. }
  142. func (vs *VolumeServer) VolumeEcShardsUnmount(ctx context.Context, req *volume_server_pb.VolumeEcShardsUnmountRequest) (*volume_server_pb.VolumeEcShardsUnmountResponse, error) {
  143. for _, shardId := range req.ShardIds {
  144. err := vs.store.UnmountEcShards(needle.VolumeId(req.VolumeId), erasure_coding.ShardId(shardId))
  145. if err != nil {
  146. glog.Errorf("ec shard unmount %v: %v", req, err)
  147. } else {
  148. glog.V(2).Infof("ec shard unmount %v", req)
  149. }
  150. if err != nil {
  151. return nil, fmt.Errorf("unmount %d.%d: %v", req.VolumeId, shardId, err)
  152. }
  153. }
  154. return &volume_server_pb.VolumeEcShardsUnmountResponse{}, nil
  155. }
  156. func (vs *VolumeServer) VolumeEcShardRead(req *volume_server_pb.VolumeEcShardReadRequest, stream volume_server_pb.VolumeServer_VolumeEcShardReadServer) error {
  157. ecVolume, found := vs.store.FindEcVolume(needle.VolumeId(req.VolumeId))
  158. if !found {
  159. return fmt.Errorf("not found ec volume id %d", req.VolumeId)
  160. }
  161. ecShard, found := ecVolume.FindEcVolumeShard(erasure_coding.ShardId(req.ShardId))
  162. if !found {
  163. return fmt.Errorf("not found ec shard %d.%d", req.VolumeId, req.ShardId)
  164. }
  165. bufSize := req.Size
  166. if bufSize > BufferSizeLimit {
  167. bufSize = BufferSizeLimit
  168. }
  169. buffer := make([]byte, bufSize)
  170. startOffset, bytesToRead := req.Offset, req.Size
  171. for bytesToRead > 0 {
  172. bytesread, err := ecShard.ReadAt(buffer, startOffset)
  173. // println(fileName, "read", bytesread, "bytes, with target", bytesToRead)
  174. if bytesread > 0 {
  175. if int64(bytesread) > bytesToRead {
  176. bytesread = int(bytesToRead)
  177. }
  178. err = stream.Send(&volume_server_pb.VolumeEcShardReadResponse{
  179. Data: buffer[:bytesread],
  180. })
  181. if err != nil {
  182. // println("sending", bytesread, "bytes err", err.Error())
  183. return err
  184. }
  185. bytesToRead -= int64(bytesread)
  186. }
  187. if err != nil {
  188. if err != io.EOF {
  189. return err
  190. }
  191. return nil
  192. }
  193. }
  194. return nil
  195. }