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.

passes.go 7.5 kB

4 months ago
4 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. package parser
  2. import (
  3. "fmt"
  4. "math"
  5. "gitlink.org.cn/cloudream/common/utils/math2"
  6. "gitlink.org.cn/cloudream/common/utils/os2"
  7. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/dag"
  8. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitchlrc"
  9. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitchlrc/ops2"
  10. stgtypes "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types"
  11. )
  12. // 计算输入流的打开范围。会把流的范围按条带大小取整
  13. func calcStreamRange(ctx *GenerateContext) {
  14. stripSize := int64(ctx.LRC.ChunkSize * ctx.LRC.K)
  15. rng := math2.Range{
  16. Offset: math.MaxInt64,
  17. }
  18. for _, to := range ctx.To {
  19. if to.GetDataIndex() == -1 {
  20. toRng := to.GetRange()
  21. rng.ExtendStart(math2.Floor(toRng.Offset, stripSize))
  22. if toRng.Length != nil {
  23. rng.ExtendEnd(math2.Ceil(toRng.Offset+*toRng.Length, stripSize))
  24. } else {
  25. rng.Length = nil
  26. }
  27. } else {
  28. toRng := to.GetRange()
  29. blkStartIndex := math2.FloorDiv(toRng.Offset, int64(ctx.LRC.ChunkSize))
  30. rng.ExtendStart(blkStartIndex * stripSize)
  31. if toRng.Length != nil {
  32. blkEndIndex := math2.CeilDiv(toRng.Offset+*toRng.Length, int64(ctx.LRC.ChunkSize))
  33. rng.ExtendEnd(blkEndIndex * stripSize)
  34. } else {
  35. rng.Length = nil
  36. }
  37. }
  38. }
  39. ctx.StreamRange = rng
  40. }
  41. func buildFromNode(ctx *GenerateContext, f ioswitchlrc.From) (ops2.FromNode, error) {
  42. var repRange math2.Range
  43. var blkRange math2.Range
  44. repRange.Offset = ctx.StreamRange.Offset
  45. blkRange.Offset = ctx.StreamRange.Offset / int64(ctx.LRC.ChunkSize*ctx.LRC.K) * int64(ctx.LRC.ChunkSize)
  46. if ctx.StreamRange.Length != nil {
  47. repRngLen := *ctx.StreamRange.Length
  48. repRange.Length = &repRngLen
  49. blkRngLen := *ctx.StreamRange.Length / int64(ctx.LRC.ChunkSize*ctx.LRC.K) * int64(ctx.LRC.ChunkSize)
  50. blkRange.Length = &blkRngLen
  51. }
  52. switch f := f.(type) {
  53. case *ioswitchlrc.FromNode:
  54. getShard := ctx.DAG.NewGetShardInfo(f.UserSpace, f.FileHash)
  55. getShard.Env().ToEnvDriver(true)
  56. read := ctx.DAG.NewBaseReadDyn(f, f.UserSpace, stgtypes.DefaultOpen())
  57. if f.DataIndex == -1 {
  58. read.Option.WithNullableLength(repRange.Offset, repRange.Length)
  59. } else {
  60. read.Option.WithNullableLength(blkRange.Offset, blkRange.Length)
  61. }
  62. if err := setEnvBySpace(read, &f.UserSpace); err != nil {
  63. return nil, fmt.Errorf("set node env by user space: %w", err)
  64. }
  65. return read, nil
  66. case *ioswitchlrc.FromDriver:
  67. n := ctx.DAG.NewFromDriver(f.Handle)
  68. n.Env().ToEnvDriver(true)
  69. if f.DataIndex == -1 {
  70. f.Handle.RangeHint.Offset = repRange.Offset
  71. f.Handle.RangeHint.Length = repRange.Length
  72. } else {
  73. f.Handle.RangeHint.Offset = blkRange.Offset
  74. f.Handle.RangeHint.Length = blkRange.Length
  75. }
  76. return n, nil
  77. default:
  78. return nil, fmt.Errorf("unsupported from type %T", f)
  79. }
  80. }
  81. func buildToNode(ctx *GenerateContext, t ioswitchlrc.To) (ops2.ToNode, error) {
  82. switch t := t.(type) {
  83. case *ioswitchlrc.ToNode:
  84. tempFileName := stgtypes.MakeTempDirPath(&t.UserSpace, os2.GenerateRandomFileName(20))
  85. write := ctx.DAG.NewBaseWrite(t, t.UserSpace, tempFileName, t.Option)
  86. if err := setEnvBySpace(write, &t.UserSpace); err != nil {
  87. return nil, fmt.Errorf("set node env by user space: %w", err)
  88. }
  89. write.Env().Pinned = true
  90. add := ctx.DAG.NewStoreShard(t.UserSpace, t.FileHashStoreKey)
  91. add.Env().ToEnvDriver(true)
  92. write.FileInfoVar().ToSlot(add.FileInfoSlot())
  93. return write, nil
  94. case *ioswitchlrc.ToDriver:
  95. n := ctx.DAG.NewToDriver(t.Handle)
  96. n.Env().ToEnvDriver(true)
  97. return n, nil
  98. default:
  99. return nil, fmt.Errorf("unsupported to type %T", t)
  100. }
  101. }
  102. // 通过流的输入输出位置来确定指令的执行位置。
  103. // To系列的指令都会有固定的执行位置,这些位置会随着pin操作逐步扩散到整个DAG,
  104. // 所以理论上不会出现有指令的位置始终无法确定的情况。
  105. func pin(ctx *GenerateContext) bool {
  106. changed := false
  107. ctx.DAG.Walk(func(node dag.Node) bool {
  108. if node.Env().Pinned {
  109. return true
  110. }
  111. var toEnv *dag.NodeEnv
  112. for _, out := range node.OutputStreams().Slots.RawArray() {
  113. for _, to := range out.Dst.RawArray() {
  114. if to.Env().Type == dag.EnvAny {
  115. continue
  116. }
  117. if toEnv == nil {
  118. toEnv = to.Env()
  119. } else if !toEnv.Equals(to.Env()) {
  120. toEnv = nil
  121. break
  122. }
  123. }
  124. }
  125. if toEnv != nil {
  126. if !node.Env().Equals(toEnv) {
  127. changed = true
  128. }
  129. *node.Env() = *toEnv
  130. return true
  131. }
  132. // 否则根据输入流的始发地来固定
  133. var fromEnv *dag.NodeEnv
  134. for _, in := range node.InputStreams().Slots.RawArray() {
  135. if in.Src.Env().Type == dag.EnvAny {
  136. continue
  137. }
  138. if fromEnv == nil {
  139. fromEnv = in.Src.Env()
  140. } else if !fromEnv.Equals(in.Src.Env()) {
  141. fromEnv = nil
  142. break
  143. }
  144. }
  145. if fromEnv != nil {
  146. if !node.Env().Equals(fromEnv) {
  147. changed = true
  148. }
  149. *node.Env() = *fromEnv
  150. }
  151. return true
  152. })
  153. return changed
  154. }
  155. // 对于所有未使用的流,增加Drop指令
  156. func dropUnused(ctx *GenerateContext) {
  157. ctx.DAG.Walk(func(node dag.Node) bool {
  158. for _, out := range node.OutputStreams().Slots.RawArray() {
  159. if out.Dst.Len() == 0 {
  160. n := ctx.DAG.NewDropStream()
  161. *n.Env() = *node.Env()
  162. n.SetInput(out)
  163. }
  164. }
  165. return true
  166. })
  167. }
  168. // 为IPFS写入指令存储结果
  169. func storeIPFSWriteResult(ctx *GenerateContext) {
  170. dag.WalkOnlyType[*ops2.StoreShardNode](ctx.DAG.Graph, func(n *ops2.StoreShardNode) bool {
  171. if n.ShardInfoKey == "" {
  172. return true
  173. }
  174. storeNode := ctx.DAG.NewStore()
  175. storeNode.Env().ToEnvDriver(true)
  176. storeNode.Store(n.ShardInfoKey, n.ShardInfoVar().Var())
  177. return true
  178. })
  179. }
  180. // 生成Range指令。StreamRange可能超过文件总大小,但Range指令会在数据量不够时不报错而是正常返回
  181. func generateRange(ctx *GenerateContext) {
  182. for i := 0; i < len(ctx.To); i++ {
  183. to := ctx.To[i]
  184. toNode := ctx.ToNodes[to]
  185. toDataIdx := to.GetDataIndex()
  186. toRng := to.GetRange()
  187. if toDataIdx == -1 {
  188. n := ctx.DAG.NewRange()
  189. toInput := toNode.Input()
  190. *n.Env() = *toInput.Var().Src.Env()
  191. rnged := n.RangeStream(toInput.Var(), math2.Range{
  192. Offset: toRng.Offset - ctx.StreamRange.Offset,
  193. Length: toRng.Length,
  194. })
  195. toInput.Var().NotTo(toNode)
  196. rnged.ToSlot(toInput)
  197. } else {
  198. stripSize := int64(ctx.LRC.ChunkSize * ctx.LRC.K)
  199. blkStartIdx := ctx.StreamRange.Offset / stripSize
  200. blkStart := blkStartIdx * int64(ctx.LRC.ChunkSize)
  201. n := ctx.DAG.NewRange()
  202. toInput := toNode.Input()
  203. *n.Env() = *toInput.Var().Src.Env()
  204. rnged := n.RangeStream(toInput.Var(), math2.Range{
  205. Offset: toRng.Offset - blkStart,
  206. Length: toRng.Length,
  207. })
  208. toInput.Var().NotTo(toNode)
  209. rnged.ToSlot(toInput)
  210. }
  211. }
  212. }
  213. // 生成Clone指令
  214. func generateClone(ctx *GenerateContext) {
  215. ctx.DAG.Walk(func(node dag.Node) bool {
  216. for _, outVar := range node.OutputStreams().Slots.RawArray() {
  217. if outVar.Dst.Len() <= 1 {
  218. continue
  219. }
  220. t := ctx.DAG.NewCloneStream()
  221. *t.Env() = *node.Env()
  222. for _, to := range outVar.Dst.RawArray() {
  223. t.NewOutput().To(to, to.InputStreams().IndexOf(outVar))
  224. }
  225. outVar.Dst.Resize(0)
  226. t.SetInput(outVar)
  227. }
  228. for _, outVar := range node.OutputValues().Slots.RawArray() {
  229. if outVar.Dst.Len() <= 1 {
  230. continue
  231. }
  232. t := ctx.DAG.NewCloneValue()
  233. *t.Env() = *node.Env()
  234. for _, to := range outVar.Dst.RawArray() {
  235. t.NewOutput().To(to, to.InputValues().IndexOf(outVar))
  236. }
  237. outVar.Dst.Resize(0)
  238. t.SetInput(outVar)
  239. }
  240. return true
  241. })
  242. }

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