You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

iterator.go 6.2 kB

7 months ago
7 months ago
5 months ago
7 months ago
7 months ago
7 months ago
5 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package downloader
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "reflect"
  7. "gitlink.org.cn/cloudream/common/pkgs/logger"
  8. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/exec"
  9. "gitlink.org.cn/cloudream/common/utils/io2"
  10. "gitlink.org.cn/cloudream/common/utils/math2"
  11. "gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader/strategy"
  12. "gitlink.org.cn/cloudream/jcs-pub/client/types"
  13. stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
  14. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2"
  15. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2"
  16. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser"
  17. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/iterator"
  18. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/publock"
  19. )
  20. type downloadSpaceInfo struct {
  21. Space types.UserSpaceDetail
  22. ObjectPinned bool
  23. Blocks []types.ObjectBlock
  24. Distance float64
  25. }
  26. type DownloadContext struct {
  27. PubLock *publock.Service
  28. }
  29. type DownloadObjectIterator struct {
  30. OnClosing func()
  31. downloader *Downloader
  32. reqs []downloadReqeust2
  33. currentIndex int
  34. }
  35. func NewDownloadObjectIterator(downloader *Downloader, downloadObjs []downloadReqeust2) *DownloadObjectIterator {
  36. return &DownloadObjectIterator{
  37. downloader: downloader,
  38. reqs: downloadObjs,
  39. }
  40. }
  41. func (i *DownloadObjectIterator) MoveNext() (*Downloading, error) {
  42. if i.currentIndex >= len(i.reqs) {
  43. return nil, iterator.ErrNoMoreItem
  44. }
  45. req := i.reqs[i.currentIndex]
  46. if req.Detail == nil {
  47. return &Downloading{
  48. Object: nil,
  49. File: nil,
  50. Request: req.Raw,
  51. }, nil
  52. }
  53. strg, err := i.downloader.selector.Select(strategy.Request{
  54. Detail: *req.Detail,
  55. Range: math2.NewRange(req.Raw.Offset, req.Raw.Length),
  56. DestLocation: stgglb.Local.Location,
  57. })
  58. if err != nil {
  59. return nil, fmt.Errorf("selecting download strategy: %w", err)
  60. }
  61. var reader io.ReadCloser
  62. switch strg := strg.(type) {
  63. case *strategy.DirectStrategy:
  64. reader, err = i.downloadDirect(req, *strg)
  65. if err != nil {
  66. return nil, fmt.Errorf("downloading object %v: %w", req.Raw.ObjectID, err)
  67. }
  68. case *strategy.ECReconstructStrategy:
  69. reader, err = i.downloadECReconstruct(req, *strg)
  70. if err != nil {
  71. return nil, fmt.Errorf("downloading ec object %v: %w", req.Raw.ObjectID, err)
  72. }
  73. case *strategy.LRCReconstructStrategy:
  74. reader, err = i.downloadLRCReconstruct(req, *strg)
  75. if err != nil {
  76. return nil, fmt.Errorf("downloading lrc object %v: %w", req.Raw.ObjectID, err)
  77. }
  78. default:
  79. return nil, fmt.Errorf("unsupported strategy type: %v", reflect.TypeOf(strg))
  80. }
  81. i.currentIndex++
  82. return &Downloading{
  83. Object: &req.Detail.Object,
  84. File: reader,
  85. Request: req.Raw,
  86. }, nil
  87. }
  88. func (i *DownloadObjectIterator) Close() {
  89. if i.OnClosing != nil {
  90. i.OnClosing()
  91. }
  92. }
  93. func (i *DownloadObjectIterator) downloadDirect(req downloadReqeust2, strg strategy.DirectStrategy) (io.ReadCloser, error) {
  94. logger.Debugf("downloading object %v from storage %v", req.Raw.ObjectID, strg.UserSpace.UserSpace.Storage.String())
  95. var strHandle *exec.DriverReadStream
  96. ft := ioswitch2.NewFromTo()
  97. toExec, handle := ioswitch2.NewToDriver(ioswitch2.RawStream())
  98. toExec.Range = math2.Range{
  99. Offset: req.Raw.Offset,
  100. }
  101. len := req.Detail.Object.Size - req.Raw.Offset
  102. if req.Raw.Length != -1 {
  103. len = req.Raw.Length
  104. toExec.Range.Length = &len
  105. }
  106. fromSpace := strg.UserSpace
  107. shouldAtClient := i.downloader.speedStats.ShouldAtClient(len)
  108. if shouldAtClient {
  109. fromSpace.RecommendHub = nil
  110. }
  111. ft.AddFrom(ioswitch2.NewFromShardstore(req.Detail.Object.FileHash, fromSpace, ioswitch2.RawStream())).AddTo(toExec)
  112. strHandle = handle
  113. plans := exec.NewPlanBuilder()
  114. if err := parser.Parse(ft, plans); err != nil {
  115. return nil, fmt.Errorf("parsing plan: %w", err)
  116. }
  117. exeCtx := exec.NewExecContext()
  118. exec.SetValueByType(exeCtx, i.downloader.stgPool)
  119. exec := plans.Execute(exeCtx)
  120. go func() {
  121. ret, err := exec.Wait(context.TODO())
  122. if err != nil {
  123. logger.Warnf("downloading object %v: %v", req.Raw.ObjectID, err)
  124. }
  125. for _, v := range ret.GetArray(ops2.BaseReadStatsStoreKey) {
  126. v2 := v.(*ops2.BaseReadStatsValue)
  127. i.downloader.speedStats.Record(v2.Size, v2.Time, v2.Location.IsDriver)
  128. }
  129. }()
  130. return exec.BeginRead(strHandle)
  131. }
  132. func (i *DownloadObjectIterator) downloadECReconstruct(req downloadReqeust2, strg strategy.ECReconstructStrategy) (io.ReadCloser, error) {
  133. var logStrs []any = []any{fmt.Sprintf("downloading ec object %v from: ", req.Raw.ObjectID)}
  134. for i, b := range strg.Blocks {
  135. if i > 0 {
  136. logStrs = append(logStrs, ", ")
  137. }
  138. logStrs = append(logStrs, fmt.Sprintf("%v@%v", b.Index, strg.UserSpaces[i].UserSpace.Storage.String()))
  139. }
  140. logger.Debug(logStrs...)
  141. length := req.Detail.Object.Size - req.Raw.Offset
  142. if req.Raw.Length != -1 {
  143. length = req.Raw.Length
  144. }
  145. shouldAtClient := i.downloader.speedStats.ShouldAtClient(length)
  146. downloadBlks := make([]downloadBlock, len(strg.Blocks))
  147. for i, b := range strg.Blocks {
  148. fromSpace := strg.UserSpaces[i]
  149. if shouldAtClient {
  150. fromSpace.RecommendHub = nil
  151. }
  152. downloadBlks[i] = downloadBlock{
  153. Block: b,
  154. Space: fromSpace,
  155. }
  156. }
  157. pr, pw := io.Pipe()
  158. go func() {
  159. readPos := req.Raw.Offset
  160. totalReadLen := req.Detail.Object.Size - req.Raw.Offset
  161. if req.Raw.Length >= 0 {
  162. totalReadLen = math2.Min(req.Raw.Length, totalReadLen)
  163. }
  164. firstStripIndex := readPos / strg.Redundancy.StripSize()
  165. stripIter := NewStripIterator(i.downloader, req.Detail.Object, downloadBlks, strg.Redundancy, firstStripIndex, i.downloader.strips, i.downloader.cfg.ECStripPrefetchCount)
  166. defer stripIter.Close()
  167. for totalReadLen > 0 {
  168. strip, err := stripIter.MoveNext()
  169. if err == iterator.ErrNoMoreItem {
  170. pw.CloseWithError(io.ErrUnexpectedEOF)
  171. return
  172. }
  173. if err != nil {
  174. pw.CloseWithError(err)
  175. return
  176. }
  177. readRelativePos := readPos - strip.Position
  178. curReadLen := math2.Min(totalReadLen, strg.Redundancy.StripSize()-readRelativePos)
  179. err = io2.WriteAll(pw, strip.Data[readRelativePos:readRelativePos+curReadLen])
  180. if err != nil {
  181. pw.CloseWithError(err)
  182. return
  183. }
  184. totalReadLen -= curReadLen
  185. readPos += curReadLen
  186. }
  187. pw.Close()
  188. }()
  189. return pr, nil
  190. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。