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.

lrc_strip_iterator.go 4.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package downloader
  2. import (
  3. "context"
  4. "io"
  5. "sync"
  6. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
  7. "gitlink.org.cn/cloudream/common/pkgs/iterator"
  8. "gitlink.org.cn/cloudream/common/pkgs/logger"
  9. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  10. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc"
  11. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc/parser"
  12. )
  13. type LRCStripIterator struct {
  14. object cdssdk.Object
  15. blocks []downloadBlock
  16. red *cdssdk.LRCRedundancy
  17. curStripIndex int64
  18. cache *StripCache
  19. dataChan chan dataChanEntry
  20. downloadingDone chan any
  21. downloadingDoneOnce sync.Once
  22. inited bool
  23. }
  24. func NewLRCStripIterator(object cdssdk.Object, blocks []downloadBlock, red *cdssdk.LRCRedundancy, beginStripIndex int64, cache *StripCache, maxPrefetch int) *LRCStripIterator {
  25. if maxPrefetch <= 0 {
  26. maxPrefetch = 1
  27. }
  28. iter := &LRCStripIterator{
  29. object: object,
  30. blocks: blocks,
  31. red: red,
  32. curStripIndex: beginStripIndex,
  33. cache: cache,
  34. dataChan: make(chan dataChanEntry, maxPrefetch-1),
  35. downloadingDone: make(chan any),
  36. }
  37. return iter
  38. }
  39. func (s *LRCStripIterator) MoveNext() (Strip, error) {
  40. if !s.inited {
  41. go s.downloading()
  42. s.inited = true
  43. }
  44. // 先尝试获取一下,用于判断本次获取是否发生了等待
  45. select {
  46. case entry, ok := <-s.dataChan:
  47. if !ok || entry.Error == io.EOF {
  48. return Strip{}, iterator.ErrNoMoreItem
  49. }
  50. if entry.Error != nil {
  51. return Strip{}, entry.Error
  52. }
  53. s.curStripIndex++
  54. return Strip{Data: entry.Data, Position: entry.Position}, nil
  55. default:
  56. logger.Debugf("waitting for ec strip %v of object %v", s.curStripIndex, s.object.ObjectID)
  57. }
  58. // 发生了等待
  59. select {
  60. case entry, ok := <-s.dataChan:
  61. if !ok || entry.Error == io.EOF {
  62. return Strip{}, iterator.ErrNoMoreItem
  63. }
  64. if entry.Error != nil {
  65. return Strip{}, entry.Error
  66. }
  67. s.curStripIndex++
  68. return Strip{Data: entry.Data, Position: entry.Position}, nil
  69. case <-s.downloadingDone:
  70. return Strip{}, iterator.ErrNoMoreItem
  71. }
  72. }
  73. func (s *LRCStripIterator) Close() {
  74. s.downloadingDoneOnce.Do(func() {
  75. close(s.downloadingDone)
  76. })
  77. }
  78. func (s *LRCStripIterator) downloading() {
  79. var froms []ioswitchlrc.From
  80. for _, b := range s.blocks {
  81. froms = append(froms, ioswitchlrc.NewFromNode(b.Block.FileHash, &b.Node, b.Block.Index))
  82. }
  83. toExec, hd := ioswitchlrc.NewToDriverWithRange(-1, exec.Range{
  84. Offset: s.curStripIndex * int64(s.red.ChunkSize*s.red.K),
  85. })
  86. plans := exec.NewPlanBuilder()
  87. err := parser.ReconstructAny(froms, []ioswitchlrc.To{toExec}, plans)
  88. if err != nil {
  89. s.sendToDataChan(dataChanEntry{Error: err})
  90. return
  91. }
  92. exec := plans.Execute()
  93. ctx, cancel := context.WithCancel(context.Background())
  94. go exec.Wait(ctx)
  95. defer cancel()
  96. str, err := exec.BeginRead(hd)
  97. if err != nil {
  98. s.sendToDataChan(dataChanEntry{Error: err})
  99. return
  100. }
  101. curStripIndex := s.curStripIndex
  102. loop:
  103. for {
  104. stripBytesPos := curStripIndex * int64(s.red.K) * int64(s.red.ChunkSize)
  105. if stripBytesPos >= s.object.Size {
  106. s.sendToDataChan(dataChanEntry{Error: io.EOF})
  107. break
  108. }
  109. stripKey := ECStripKey{
  110. ObjectID: s.object.ObjectID,
  111. StripIndex: curStripIndex,
  112. }
  113. item, ok := s.cache.Get(stripKey)
  114. if ok {
  115. if item.ObjectFileHash == s.object.FileHash {
  116. if !s.sendToDataChan(dataChanEntry{Data: item.Data, Position: stripBytesPos}) {
  117. break loop
  118. }
  119. curStripIndex++
  120. continue
  121. } else {
  122. // 如果Object的Hash和Cache的Hash不一致,说明Cache是无效的,需要重新下载
  123. s.cache.Remove(stripKey)
  124. }
  125. }
  126. dataBuf := make([]byte, int64(s.red.K*s.red.ChunkSize))
  127. n, err := io.ReadFull(str, dataBuf)
  128. if err == io.ErrUnexpectedEOF {
  129. s.cache.Add(stripKey, ObjectECStrip{
  130. Data: dataBuf,
  131. ObjectFileHash: s.object.FileHash,
  132. })
  133. s.sendToDataChan(dataChanEntry{Data: dataBuf[:n], Position: stripBytesPos})
  134. s.sendToDataChan(dataChanEntry{Error: io.EOF})
  135. break loop
  136. }
  137. if err != nil {
  138. s.sendToDataChan(dataChanEntry{Error: err})
  139. break loop
  140. }
  141. s.cache.Add(stripKey, ObjectECStrip{
  142. Data: dataBuf,
  143. ObjectFileHash: s.object.FileHash,
  144. })
  145. if !s.sendToDataChan(dataChanEntry{Data: dataBuf, Position: stripBytesPos}) {
  146. break loop
  147. }
  148. curStripIndex++
  149. }
  150. close(s.dataChan)
  151. }
  152. func (s *LRCStripIterator) sendToDataChan(entry dataChanEntry) bool {
  153. select {
  154. case s.dataChan <- entry:
  155. return true
  156. case <-s.downloadingDone:
  157. return false
  158. }
  159. }

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