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.5 kB

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

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