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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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/storage/common/pkgs/ioswitchlrc"
  9. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc/ops2"
  10. )
  11. type GenerateContext struct {
  12. LRC cdssdk.LRCRedundancy
  13. DAG *dag.Graph
  14. Toes []ioswitchlrc.To
  15. StreamRange exec.Range
  16. }
  17. // 输入一个完整文件,从这个完整文件产生任意文件块(也可再产生完整文件)。
  18. func Encode(fr ioswitchlrc.From, toes []ioswitchlrc.To, blder *exec.PlanBuilder) error {
  19. if fr.GetDataIndex() != -1 {
  20. return fmt.Errorf("from data is not a complete file")
  21. }
  22. ctx := GenerateContext{
  23. LRC: cdssdk.DefaultLRCRedundancy,
  24. DAG: dag.NewGraph(),
  25. Toes: toes,
  26. }
  27. calcStreamRange(&ctx)
  28. err := buildDAGEncode(&ctx, fr, toes)
  29. if err != nil {
  30. return err
  31. }
  32. // 确定指令执行位置的过程,也需要反复进行,直到没有变化为止。
  33. for pin(&ctx) {
  34. }
  35. // 下面这些只需要执行一次,但需要按顺序
  36. dropUnused(&ctx)
  37. storeIPFSWriteResult(&ctx)
  38. generateClone(&ctx)
  39. generateRange(&ctx)
  40. return plan.Generate(ctx.DAG, blder)
  41. }
  42. func buildDAGEncode(ctx *GenerateContext, fr ioswitchlrc.From, toes []ioswitchlrc.To) error {
  43. frNode, err := buildFromNode(ctx, fr)
  44. if err != nil {
  45. return fmt.Errorf("building from node: %w", err)
  46. }
  47. var dataToes []ioswitchlrc.To
  48. var parityToes []ioswitchlrc.To
  49. // 先创建需要完整文件的To节点,同时统计一下需要哪些文件块
  50. for _, to := range toes {
  51. idx := to.GetDataIndex()
  52. if idx == -1 {
  53. toNode, err := buildToNode(ctx, to)
  54. if err != nil {
  55. return fmt.Errorf("building to node: %w", err)
  56. }
  57. frNode.OutputStreams[0].To(toNode, 0)
  58. } else if idx < ctx.LRC.K {
  59. dataToes = append(dataToes, to)
  60. } else {
  61. parityToes = append(parityToes, to)
  62. }
  63. }
  64. if len(dataToes) == 0 && len(parityToes) == 0 {
  65. return nil
  66. }
  67. // 需要文件块,则生成Split指令
  68. splitNode := ctx.DAG.NewNode(&ops2.ChunkedSplitType{
  69. OutputCount: ctx.LRC.K,
  70. ChunkSize: ctx.LRC.ChunkSize,
  71. }, &ioswitchlrc.NodeProps{})
  72. frNode.OutputStreams[0].To(splitNode, 0)
  73. for _, to := range dataToes {
  74. toNode, err := buildToNode(ctx, to)
  75. if err != nil {
  76. return fmt.Errorf("building to node: %w", err)
  77. }
  78. splitNode.OutputStreams[to.GetDataIndex()].To(toNode, 0)
  79. }
  80. if len(parityToes) == 0 {
  81. return nil
  82. }
  83. // 需要校验块,则进一步生成Construct指令
  84. conNode, conType := dag.NewNode(ctx.DAG, &ops2.LRCConstructAnyType{
  85. LRC: ctx.LRC,
  86. }, &ioswitchlrc.NodeProps{})
  87. for _, out := range splitNode.OutputStreams {
  88. conType.AddInput(conNode, out, ioswitchlrc.SProps(out).StreamIndex)
  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. conType.NewOutput(conNode, to.GetDataIndex()).To(toNode, 0)
  96. }
  97. return nil
  98. }
  99. // 提供数据块+编码块中的k个块,重建任意块,包括完整文件。
  100. func ReconstructAny(frs []ioswitchlrc.From, toes []ioswitchlrc.To, blder *exec.PlanBuilder) error {
  101. ctx := GenerateContext{
  102. LRC: cdssdk.DefaultLRCRedundancy,
  103. DAG: dag.NewGraph(),
  104. Toes: toes,
  105. }
  106. calcStreamRange(&ctx)
  107. err := buildDAGReconstructAny(&ctx, frs, toes)
  108. if err != nil {
  109. return err
  110. }
  111. // 确定指令执行位置的过程,也需要反复进行,直到没有变化为止。
  112. for pin(&ctx) {
  113. }
  114. // 下面这些只需要执行一次,但需要按顺序
  115. dropUnused(&ctx)
  116. storeIPFSWriteResult(&ctx)
  117. generateClone(&ctx)
  118. generateRange(&ctx)
  119. return plan.Generate(ctx.DAG, blder)
  120. }
  121. func buildDAGReconstructAny(ctx *GenerateContext, frs []ioswitchlrc.From, toes []ioswitchlrc.To) error {
  122. frNodes := make(map[int]*dag.Node)
  123. for _, fr := range frs {
  124. frNode, err := buildFromNode(ctx, fr)
  125. if err != nil {
  126. return fmt.Errorf("building from node: %w", err)
  127. }
  128. frNodes[fr.GetDataIndex()] = frNode
  129. }
  130. var completeToes []ioswitchlrc.To
  131. var missedToes []ioswitchlrc.To
  132. // 先创建需要完整文件的To节点,同时统计一下需要哪些文件块
  133. for _, to := range toes {
  134. toIdx := to.GetDataIndex()
  135. fr := frNodes[toIdx]
  136. if fr != nil {
  137. node, err := buildToNode(ctx, to)
  138. if err != nil {
  139. return fmt.Errorf("building to node: %w", err)
  140. }
  141. fr.OutputStreams[0].To(node, 0)
  142. continue
  143. }
  144. if toIdx == -1 {
  145. completeToes = append(completeToes, to)
  146. } else {
  147. missedToes = append(missedToes, to)
  148. }
  149. }
  150. if len(completeToes) == 0 && len(missedToes) == 0 {
  151. return nil
  152. }
  153. // 生成Construct指令来恢复缺少的块
  154. conNode, conType := dag.NewNode(ctx.DAG, &ops2.LRCConstructAnyType{
  155. LRC: ctx.LRC,
  156. }, &ioswitchlrc.NodeProps{})
  157. for _, fr := range frNodes {
  158. conType.AddInput(conNode, fr.OutputStreams[0], ioswitchlrc.SProps(fr.OutputStreams[0]).StreamIndex)
  159. }
  160. for _, to := range missedToes {
  161. toNode, err := buildToNode(ctx, to)
  162. if err != nil {
  163. return fmt.Errorf("building to node: %w", err)
  164. }
  165. conType.NewOutput(conNode, to.GetDataIndex()).To(toNode, 0)
  166. }
  167. if len(completeToes) == 0 {
  168. return nil
  169. }
  170. // 需要完整文件,则生成Join指令
  171. joinNode := ctx.DAG.NewNode(&ops2.ChunkedJoinType{
  172. InputCount: ctx.LRC.K,
  173. ChunkSize: ctx.LRC.ChunkSize,
  174. }, &ioswitchlrc.NodeProps{})
  175. for i := 0; i < ctx.LRC.K; i++ {
  176. n := frNodes[i]
  177. if n == nil {
  178. conType.NewOutput(conNode, i).To(joinNode, i)
  179. } else {
  180. n.OutputStreams[0].To(joinNode, i)
  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. joinNode.OutputStreams[0].To(toNode, 0)
  189. }
  190. // 如果不需要Construct任何块,则删除这个节点
  191. if len(conNode.OutputStreams) == 0 {
  192. conType.RemoveAllInputs(conNode)
  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: dag.NewGraph(),
  202. Toes: toes,
  203. }
  204. calcStreamRange(&ctx)
  205. err := buildDAGReconstructGroup(&ctx, frs, toes)
  206. if err != nil {
  207. return err
  208. }
  209. // 确定指令执行位置的过程,也需要反复进行,直到没有变化为止。
  210. for pin(&ctx) {
  211. }
  212. // 下面这些只需要执行一次,但需要按顺序
  213. dropUnused(&ctx)
  214. storeIPFSWriteResult(&ctx)
  215. generateClone(&ctx)
  216. generateRange(&ctx)
  217. return plan.Generate(ctx.DAG, blder)
  218. }
  219. func buildDAGReconstructGroup(ctx *GenerateContext, frs []ioswitchlrc.From, toes []ioswitchlrc.To) error {
  220. missedGrpIdx := toes[0].GetDataIndex()
  221. conNode := ctx.DAG.NewNode(&ops2.LRCConstructGroupType{
  222. LRC: ctx.LRC,
  223. TargetBlockIndex: missedGrpIdx,
  224. }, &ioswitchlrc.NodeProps{})
  225. for i, fr := range frs {
  226. frNode, err := buildFromNode(ctx, fr)
  227. if err != nil {
  228. return fmt.Errorf("building from node: %w", err)
  229. }
  230. frNode.OutputStreams[0].To(conNode, i)
  231. }
  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. conNode.OutputStreams[0].To(toNode, 0)
  238. }
  239. return nil
  240. }

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