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.

generator.go 7.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package parser
  2. import (
  3. "fmt"
  4. "gitlink.org.cn/cloudream/common/utils/math2"
  5. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/dag"
  6. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/exec"
  7. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/plan"
  8. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitchlrc"
  9. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitchlrc/ops2"
  10. jcstypes "gitlink.org.cn/cloudream/jcs-pub/common/types"
  11. )
  12. type GenerateContext struct {
  13. LRC jcstypes.LRCRedundancy
  14. DAG *ops2.GraphNodeBuilder
  15. To []ioswitchlrc.To
  16. ToNodes map[ioswitchlrc.To]ops2.ToNode
  17. StreamRange math2.Range
  18. }
  19. // 输入一个完整文件,从这个完整文件产生任意文件块(也可再产生完整文件)。
  20. func Encode(fr ioswitchlrc.From, toes []ioswitchlrc.To, blder *exec.PlanBuilder) error {
  21. if fr.GetDataIndex() != -1 {
  22. return fmt.Errorf("from data is not a complete file")
  23. }
  24. ctx := GenerateContext{
  25. LRC: jcstypes.DefaultLRCRedundancy,
  26. DAG: ops2.NewGraphNodeBuilder(),
  27. To: toes,
  28. ToNodes: make(map[ioswitchlrc.To]ops2.ToNode),
  29. }
  30. calcStreamRange(&ctx)
  31. err := buildDAGEncode(&ctx, fr, toes)
  32. if err != nil {
  33. return err
  34. }
  35. // 确定指令执行位置的过程,也需要反复进行,直到没有变化为止。
  36. for pin(&ctx) {
  37. }
  38. // 下面这些只需要执行一次,但需要按顺序
  39. dropUnused(&ctx)
  40. storeIPFSWriteResult(&ctx)
  41. generateClone(&ctx)
  42. generateRange(&ctx)
  43. // TODO 设置StatsCtx
  44. return plan.Compile(ctx.DAG.Graph, blder, plan.CompileOption{})
  45. }
  46. func buildDAGEncode(ctx *GenerateContext, fr ioswitchlrc.From, toes []ioswitchlrc.To) error {
  47. frNode, err := buildFromNode(ctx, fr)
  48. if err != nil {
  49. return fmt.Errorf("building from node: %w", err)
  50. }
  51. var dataToes []ioswitchlrc.To
  52. var parityToes []ioswitchlrc.To
  53. // 先创建需要完整文件的To节点,同时统计一下需要哪些文件块
  54. for _, to := range toes {
  55. idx := to.GetDataIndex()
  56. if idx == -1 {
  57. toNode, err := buildToNode(ctx, to)
  58. if err != nil {
  59. return fmt.Errorf("building to node: %w", err)
  60. }
  61. ctx.ToNodes[to] = toNode
  62. frNode.Output().ToSlot(toNode.Input())
  63. } else if idx < ctx.LRC.K {
  64. dataToes = append(dataToes, to)
  65. } else {
  66. parityToes = append(parityToes, to)
  67. }
  68. }
  69. if len(dataToes) == 0 && len(parityToes) == 0 {
  70. return nil
  71. }
  72. // 需要文件块,则生成Split指令
  73. splitNode := ctx.DAG.NewChunkedSplit(ctx.LRC.ChunkSize, ctx.LRC.K)
  74. splitNode.Split(frNode.Output().Var())
  75. for _, to := range dataToes {
  76. toNode, err := buildToNode(ctx, to)
  77. if err != nil {
  78. return fmt.Errorf("building to node: %w", err)
  79. }
  80. ctx.ToNodes[to] = toNode
  81. splitNode.SubStream(to.GetDataIndex()).ToSlot(toNode.Input())
  82. }
  83. if len(parityToes) == 0 {
  84. return nil
  85. }
  86. // 需要校验块,则进一步生成Construct指令
  87. conType := ctx.DAG.NewLRCConstructAny(ctx.LRC)
  88. for i, out := range splitNode.OutputStreams().Slots.RawArray() {
  89. conType.AddInput(out, i)
  90. }
  91. for _, to := range parityToes {
  92. toNode, err := buildToNode(ctx, to)
  93. if err != nil {
  94. return fmt.Errorf("building to node: %w", err)
  95. }
  96. ctx.ToNodes[to] = toNode
  97. conType.NewOutput(to.GetDataIndex()).ToSlot(toNode.Input())
  98. }
  99. return nil
  100. }
  101. // 提供数据块+编码块中的k个块,重建任意块,包括完整文件。
  102. func ReconstructAny(frs []ioswitchlrc.From, toes []ioswitchlrc.To, blder *exec.PlanBuilder) error {
  103. ctx := GenerateContext{
  104. LRC: jcstypes.DefaultLRCRedundancy,
  105. DAG: ops2.NewGraphNodeBuilder(),
  106. To: toes,
  107. ToNodes: make(map[ioswitchlrc.To]ops2.ToNode),
  108. }
  109. calcStreamRange(&ctx)
  110. err := buildDAGReconstructAny(&ctx, frs, toes)
  111. if err != nil {
  112. return err
  113. }
  114. // 确定指令执行位置的过程,也需要反复进行,直到没有变化为止。
  115. for pin(&ctx) {
  116. }
  117. // 下面这些只需要执行一次,但需要按顺序
  118. dropUnused(&ctx)
  119. storeIPFSWriteResult(&ctx)
  120. generateClone(&ctx)
  121. generateRange(&ctx)
  122. return plan.Compile(ctx.DAG.Graph, blder, plan.CompileOption{})
  123. }
  124. func buildDAGReconstructAny(ctx *GenerateContext, frs []ioswitchlrc.From, toes []ioswitchlrc.To) error {
  125. frNodes := make(map[int]ops2.FromNode)
  126. for _, fr := range frs {
  127. frNode, err := buildFromNode(ctx, fr)
  128. if err != nil {
  129. return fmt.Errorf("building from node: %w", err)
  130. }
  131. frNodes[fr.GetDataIndex()] = frNode
  132. }
  133. var completeToes []ioswitchlrc.To
  134. var missedToes []ioswitchlrc.To
  135. // 先创建需要完整文件的To节点,同时统计一下需要哪些文件块
  136. for _, to := range toes {
  137. toIdx := to.GetDataIndex()
  138. fr := frNodes[toIdx]
  139. if fr != nil {
  140. toNode, err := buildToNode(ctx, to)
  141. if err != nil {
  142. return fmt.Errorf("building to node: %w", err)
  143. }
  144. ctx.ToNodes[to] = toNode
  145. fr.Output().ToSlot(toNode.Input())
  146. continue
  147. }
  148. if toIdx == -1 {
  149. completeToes = append(completeToes, to)
  150. } else {
  151. missedToes = append(missedToes, to)
  152. }
  153. }
  154. if len(completeToes) == 0 && len(missedToes) == 0 {
  155. return nil
  156. }
  157. // 生成Construct指令来恢复缺少的块
  158. conNode := ctx.DAG.NewLRCConstructAny(ctx.LRC)
  159. for i, fr := range frNodes {
  160. conNode.AddInput(fr.Output().Var(), i)
  161. }
  162. for _, to := range missedToes {
  163. toNode, err := buildToNode(ctx, to)
  164. if err != nil {
  165. return fmt.Errorf("building to node: %w", err)
  166. }
  167. ctx.ToNodes[to] = toNode
  168. conNode.NewOutput(to.GetDataIndex()).ToSlot(toNode.Input())
  169. }
  170. if len(completeToes) == 0 {
  171. return nil
  172. }
  173. // 需要完整文件,则生成Join指令
  174. joinNode := ctx.DAG.NewChunkedJoin(ctx.LRC.ChunkSize)
  175. for i := 0; i < ctx.LRC.K; i++ {
  176. fr := frNodes[i]
  177. if fr == nil {
  178. joinNode.AddInput(conNode.NewOutput(i))
  179. } else {
  180. joinNode.AddInput(fr.Output().Var())
  181. }
  182. }
  183. for _, to := range completeToes {
  184. toNode, err := buildToNode(ctx, to)
  185. if err != nil {
  186. return fmt.Errorf("building to node: %w", err)
  187. }
  188. ctx.ToNodes[to] = toNode
  189. joinNode.Joined().ToSlot(toNode.Input())
  190. }
  191. // 如果不需要Construct任何块,则删除这个节点
  192. if conNode.OutputStreams().Len() == 0 {
  193. conNode.RemoveAllInputs()
  194. ctx.DAG.RemoveNode(conNode)
  195. }
  196. return nil
  197. }
  198. // 输入同一组的多个块,恢复出剩下缺少的一个块。
  199. func ReconstructGroup(frs []ioswitchlrc.From, toes []ioswitchlrc.To, blder *exec.PlanBuilder) error {
  200. ctx := GenerateContext{
  201. LRC: jcstypes.DefaultLRCRedundancy,
  202. DAG: ops2.NewGraphNodeBuilder(),
  203. To: toes,
  204. ToNodes: make(map[ioswitchlrc.To]ops2.ToNode),
  205. }
  206. calcStreamRange(&ctx)
  207. err := buildDAGReconstructGroup(&ctx, frs, toes)
  208. if err != nil {
  209. return err
  210. }
  211. // 确定指令执行位置的过程,也需要反复进行,直到没有变化为止。
  212. for pin(&ctx) {
  213. }
  214. // 下面这些只需要执行一次,但需要按顺序
  215. dropUnused(&ctx)
  216. storeIPFSWriteResult(&ctx)
  217. generateClone(&ctx)
  218. generateRange(&ctx)
  219. return plan.Compile(ctx.DAG.Graph, blder, plan.CompileOption{})
  220. }
  221. func buildDAGReconstructGroup(ctx *GenerateContext, frs []ioswitchlrc.From, toes []ioswitchlrc.To) error {
  222. var inputs []*dag.StreamVar
  223. for _, fr := range frs {
  224. frNode, err := buildFromNode(ctx, fr)
  225. if err != nil {
  226. return fmt.Errorf("building from node: %w", err)
  227. }
  228. inputs = append(inputs, frNode.Output().Var())
  229. }
  230. missedGrpIdx := toes[0].GetDataIndex()
  231. conNode := ctx.DAG.NewLRCConstructGroup(ctx.LRC)
  232. missedBlk := conNode.SetupForTarget(missedGrpIdx, inputs)
  233. for _, to := range toes {
  234. toNode, err := buildToNode(ctx, to)
  235. if err != nil {
  236. return fmt.Errorf("building to node: %w", err)
  237. }
  238. ctx.ToNodes[to] = toNode
  239. missedBlk.ToSlot(toNode.Input())
  240. }
  241. return nil
  242. }

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