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.

parser.go 26 kB

11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. package parser
  2. import (
  3. "fmt"
  4. "math"
  5. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag"
  6. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
  7. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/plan"
  8. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  9. "gitlink.org.cn/cloudream/common/utils/lo2"
  10. "gitlink.org.cn/cloudream/common/utils/math2"
  11. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2"
  12. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2"
  13. "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types"
  14. )
  15. type IndexedStream struct {
  16. Stream *dag.StreamVar
  17. StreamIndex ioswitch2.StreamIndex
  18. }
  19. type ParseContext struct {
  20. Ft ioswitch2.FromTo
  21. DAG *ops2.GraphNodeBuilder
  22. // 为了产生所有To所需的数据范围,而需要From打开的范围。
  23. // 这个范围是基于整个文件的,且上下界都取整到条带大小的整数倍,因此上界是有可能超过文件大小的。
  24. ToNodes map[ioswitch2.To]ops2.ToNode
  25. IndexedStreams []IndexedStream
  26. StreamRange exec.Range
  27. UseEC bool // 是否使用纠删码
  28. UseSegment bool // 是否使用分段
  29. }
  30. func Parse(ft ioswitch2.FromTo, blder *exec.PlanBuilder) error {
  31. ctx := ParseContext{
  32. Ft: ft,
  33. DAG: ops2.NewGraphNodeBuilder(),
  34. ToNodes: make(map[ioswitch2.To]ops2.ToNode),
  35. }
  36. // 分成两个阶段:
  37. // 1. 基于From和To生成更多指令,初步匹配to的需求
  38. err := checkEncodingParams(&ctx)
  39. if err != nil {
  40. return err
  41. }
  42. // 计算一下打开流的范围
  43. calcStreamRange(&ctx)
  44. err = extend(&ctx)
  45. if err != nil {
  46. return err
  47. }
  48. // 2. 优化上一步生成的指令
  49. err = fixSegmentJoin(&ctx)
  50. if err != nil {
  51. return err
  52. }
  53. err = fixSegmentSplit(&ctx)
  54. if err != nil {
  55. return err
  56. }
  57. // 对于删除指令的优化,需要反复进行,直到没有变化为止。
  58. // 从目前实现上来说不会死循环
  59. for {
  60. opted := false
  61. if removeUnusedJoin(&ctx) {
  62. opted = true
  63. }
  64. if removeUnusedMultiplyOutput(&ctx) {
  65. opted = true
  66. }
  67. if removeUnusedSplit(&ctx) {
  68. opted = true
  69. }
  70. if omitSplitJoin(&ctx) {
  71. opted = true
  72. }
  73. if removeUnusedSegmentJoin(&ctx) {
  74. opted = true
  75. }
  76. if removeUnusedSegmentSplit(&ctx) {
  77. opted = true
  78. }
  79. if omitSegmentSplitJoin(&ctx) {
  80. opted = true
  81. }
  82. if !opted {
  83. break
  84. }
  85. }
  86. // 确定指令执行位置的过程,也需要反复进行,直到没有变化为止。
  87. for pin(&ctx) {
  88. }
  89. // 下面这些只需要执行一次,但需要按顺序
  90. removeUnusedFromNode(&ctx)
  91. dropUnused(&ctx)
  92. storeIPFSWriteResult(&ctx)
  93. generateRange(&ctx)
  94. generateClone(&ctx)
  95. return plan.Generate(ctx.DAG.Graph, blder)
  96. }
  97. func findOutputStream(ctx *ParseContext, streamIndex ioswitch2.StreamIndex) *dag.StreamVar {
  98. var ret *dag.StreamVar
  99. for _, s := range ctx.IndexedStreams {
  100. if s.StreamIndex == streamIndex {
  101. ret = s.Stream
  102. break
  103. }
  104. }
  105. return ret
  106. }
  107. // 检查使用不同编码时参数是否设置到位
  108. func checkEncodingParams(ctx *ParseContext) error {
  109. for _, f := range ctx.Ft.Froms {
  110. if f.GetStreamIndex().IsEC() {
  111. ctx.UseEC = true
  112. if ctx.Ft.ECParam == nil {
  113. return fmt.Errorf("EC encoding parameters not set")
  114. }
  115. }
  116. if f.GetStreamIndex().IsSegment() {
  117. ctx.UseSegment = true
  118. if ctx.Ft.SegmentParam == nil {
  119. return fmt.Errorf("segment parameters not set")
  120. }
  121. }
  122. }
  123. for _, t := range ctx.Ft.Toes {
  124. if t.GetStreamIndex().IsEC() {
  125. ctx.UseEC = true
  126. if ctx.Ft.ECParam == nil {
  127. return fmt.Errorf("EC encoding parameters not set")
  128. }
  129. }
  130. if t.GetStreamIndex().IsSegment() {
  131. ctx.UseSegment = true
  132. if ctx.Ft.SegmentParam == nil {
  133. return fmt.Errorf("segment parameters not set")
  134. }
  135. }
  136. }
  137. return nil
  138. }
  139. // 计算输入流的打开范围。如果From或者To中包含EC的流,则会将打开范围扩大到条带大小的整数倍。
  140. func calcStreamRange(ctx *ParseContext) {
  141. rng := exec.NewRange(math.MaxInt64, 0)
  142. for _, to := range ctx.Ft.Toes {
  143. strIdx := to.GetStreamIndex()
  144. if strIdx.IsRaw() {
  145. toRng := to.GetRange()
  146. rng.ExtendStart(toRng.Offset)
  147. if toRng.Length != nil {
  148. rng.ExtendEnd(toRng.Offset + *toRng.Length)
  149. } else {
  150. rng.Length = nil
  151. }
  152. } else if strIdx.IsEC() {
  153. toRng := to.GetRange()
  154. stripSize := ctx.Ft.ECParam.StripSize()
  155. blkStartIndex := math2.FloorDiv(toRng.Offset, int64(ctx.Ft.ECParam.ChunkSize))
  156. rng.ExtendStart(blkStartIndex * stripSize)
  157. if toRng.Length != nil {
  158. blkEndIndex := math2.CeilDiv(toRng.Offset+*toRng.Length, int64(ctx.Ft.ECParam.ChunkSize))
  159. rng.ExtendEnd(blkEndIndex * stripSize)
  160. } else {
  161. rng.Length = nil
  162. }
  163. } else if strIdx.IsSegment() {
  164. // Segment节点的Range是相对于本段的,需要加上本段的起始位置
  165. toRng := to.GetRange()
  166. segStart := ctx.Ft.SegmentParam.CalcSegmentStart(strIdx.Index)
  167. offset := toRng.Offset + segStart
  168. rng.ExtendStart(offset)
  169. if toRng.Length != nil {
  170. rng.ExtendEnd(offset + *toRng.Length)
  171. } else {
  172. rng.Length = nil
  173. }
  174. }
  175. }
  176. if ctx.UseEC {
  177. stripSize := ctx.Ft.ECParam.StripSize()
  178. rng.ExtendStart(math2.Floor(rng.Offset, stripSize))
  179. if rng.Length != nil {
  180. rng.ExtendEnd(math2.Ceil(rng.Offset+*rng.Length, stripSize))
  181. }
  182. }
  183. ctx.StreamRange = rng
  184. }
  185. func extend(ctx *ParseContext) error {
  186. for _, fr := range ctx.Ft.Froms {
  187. frNode, err := buildFromNode(ctx, fr)
  188. if err != nil {
  189. return err
  190. }
  191. ctx.IndexedStreams = append(ctx.IndexedStreams, IndexedStream{
  192. Stream: frNode.Output().Var,
  193. StreamIndex: fr.GetStreamIndex(),
  194. })
  195. // 对于完整文件的From,生成Split指令
  196. if fr.GetStreamIndex().IsRaw() {
  197. // 只有输入输出需要EC编码的块时,才生成相关指令
  198. if ctx.UseEC {
  199. splitNode := ctx.DAG.NewChunkedSplit(ctx.Ft.ECParam.ChunkSize, ctx.Ft.ECParam.K)
  200. splitNode.Split(frNode.Output().Var)
  201. for i := 0; i < ctx.Ft.ECParam.K; i++ {
  202. ctx.IndexedStreams = append(ctx.IndexedStreams, IndexedStream{
  203. Stream: splitNode.SubStream(i),
  204. StreamIndex: ioswitch2.ECSrteam(i),
  205. })
  206. }
  207. }
  208. // 同上
  209. if ctx.UseSegment {
  210. splitNode := ctx.DAG.NewSegmentSplit(ctx.Ft.SegmentParam.Segments)
  211. splitNode.SetInput(frNode.Output().Var)
  212. for i := 0; i < len(ctx.Ft.SegmentParam.Segments); i++ {
  213. ctx.IndexedStreams = append(ctx.IndexedStreams, IndexedStream{
  214. Stream: splitNode.Segment(i),
  215. StreamIndex: ioswitch2.SegmentStream(i),
  216. })
  217. }
  218. }
  219. }
  220. }
  221. if ctx.UseEC {
  222. // 如果有K个不同的文件块流,则生成Multiply指令,同时针对其生成的流,生成Join指令
  223. ecInputStrs := make(map[int]*dag.StreamVar)
  224. for _, s := range ctx.IndexedStreams {
  225. if s.StreamIndex.IsEC() && ecInputStrs[s.StreamIndex.Index] == nil {
  226. ecInputStrs[s.StreamIndex.Index] = s.Stream
  227. if len(ecInputStrs) == ctx.Ft.ECParam.K {
  228. break
  229. }
  230. }
  231. }
  232. if len(ecInputStrs) == ctx.Ft.ECParam.K {
  233. mulNode := ctx.DAG.NewECMultiply(*ctx.Ft.ECParam)
  234. for i, s := range ecInputStrs {
  235. mulNode.AddInput(s, i)
  236. }
  237. for i := 0; i < ctx.Ft.ECParam.N; i++ {
  238. ctx.IndexedStreams = append(ctx.IndexedStreams, IndexedStream{
  239. Stream: mulNode.NewOutput(i),
  240. StreamIndex: ioswitch2.ECSrteam(i),
  241. })
  242. }
  243. joinNode := ctx.DAG.NewChunkedJoin(ctx.Ft.ECParam.ChunkSize)
  244. for i := 0; i < ctx.Ft.ECParam.K; i++ {
  245. // 不可能找不到流
  246. joinNode.AddInput(findOutputStream(ctx, ioswitch2.ECSrteam(i)))
  247. }
  248. ctx.IndexedStreams = append(ctx.IndexedStreams, IndexedStream{
  249. Stream: joinNode.Joined(),
  250. StreamIndex: ioswitch2.RawStream(),
  251. })
  252. }
  253. }
  254. if ctx.UseSegment {
  255. // 先假设有所有的顺序分段,生成Join指令,后续根据Range再实际计算是否缺少流
  256. joinNode := ctx.DAG.NewSegmentJoin(ctx.Ft.SegmentParam.Segments)
  257. for i := 0; i < ctx.Ft.SegmentParam.SegmentCount(); i++ {
  258. str := findOutputStream(ctx, ioswitch2.SegmentStream(i))
  259. if str != nil {
  260. joinNode.SetInput(i, str)
  261. }
  262. }
  263. ctx.IndexedStreams = append(ctx.IndexedStreams, IndexedStream{
  264. Stream: joinNode.Joined(),
  265. StreamIndex: ioswitch2.RawStream(),
  266. })
  267. // SegmentJoin生成的Join指令可以用来生成EC块
  268. if ctx.UseEC {
  269. splitNode := ctx.DAG.NewChunkedSplit(ctx.Ft.ECParam.ChunkSize, ctx.Ft.ECParam.K)
  270. splitNode.Split(joinNode.Joined())
  271. mulNode := ctx.DAG.NewECMultiply(*ctx.Ft.ECParam)
  272. for i := 0; i < ctx.Ft.ECParam.K; i++ {
  273. mulNode.AddInput(splitNode.SubStream(i), i)
  274. ctx.IndexedStreams = append(ctx.IndexedStreams, IndexedStream{
  275. Stream: splitNode.SubStream(i),
  276. StreamIndex: ioswitch2.ECSrteam(i),
  277. })
  278. }
  279. for i := 0; i < ctx.Ft.ECParam.N; i++ {
  280. ctx.IndexedStreams = append(ctx.IndexedStreams, IndexedStream{
  281. Stream: mulNode.NewOutput(i),
  282. StreamIndex: ioswitch2.ECSrteam(i),
  283. })
  284. }
  285. }
  286. }
  287. // 为每一个To找到一个输入流
  288. for _, to := range ctx.Ft.Toes {
  289. toNode, err := buildToNode(ctx, to)
  290. if err != nil {
  291. return err
  292. }
  293. ctx.ToNodes[to] = toNode
  294. str := findOutputStream(ctx, to.GetStreamIndex())
  295. if str == nil {
  296. return fmt.Errorf("no output stream found for data index %d", to.GetStreamIndex())
  297. }
  298. toNode.SetInput(str)
  299. }
  300. return nil
  301. }
  302. func buildFromNode(ctx *ParseContext, f ioswitch2.From) (ops2.FromNode, error) {
  303. var repRange exec.Range
  304. repRange.Offset = ctx.StreamRange.Offset
  305. if ctx.StreamRange.Length != nil {
  306. repRngLen := *ctx.StreamRange.Length
  307. repRange.Length = &repRngLen
  308. }
  309. var blkRange exec.Range
  310. if ctx.UseEC {
  311. blkRange.Offset = ctx.StreamRange.Offset / int64(ctx.Ft.ECParam.ChunkSize*ctx.Ft.ECParam.K) * int64(ctx.Ft.ECParam.ChunkSize)
  312. if ctx.StreamRange.Length != nil {
  313. blkRngLen := *ctx.StreamRange.Length / int64(ctx.Ft.ECParam.ChunkSize*ctx.Ft.ECParam.K) * int64(ctx.Ft.ECParam.ChunkSize)
  314. blkRange.Length = &blkRngLen
  315. }
  316. }
  317. switch f := f.(type) {
  318. case *ioswitch2.FromShardstore:
  319. t := ctx.DAG.NewShardRead(f, f.Storage.StorageID, types.NewOpen(f.FileHash))
  320. if f.StreamIndex.IsRaw() {
  321. t.Open.WithNullableLength(repRange.Offset, repRange.Length)
  322. } else if f.StreamIndex.IsEC() {
  323. t.Open.WithNullableLength(blkRange.Offset, blkRange.Length)
  324. } else if f.StreamIndex.IsSegment() {
  325. segStart := ctx.Ft.SegmentParam.CalcSegmentStart(f.StreamIndex.Index)
  326. segLen := ctx.Ft.SegmentParam.Segments[f.StreamIndex.Index]
  327. segEnd := segStart + segLen
  328. // 打开的范围不超过本段的范围
  329. openOff := ctx.StreamRange.Offset - segStart
  330. openOff = math2.Clamp(openOff, 0, segLen)
  331. openLen := segLen
  332. if ctx.StreamRange.Length != nil {
  333. strEnd := ctx.StreamRange.Offset + *ctx.StreamRange.Length
  334. openEnd := math2.Min(strEnd, segEnd)
  335. openLen = openEnd - segStart - openOff
  336. }
  337. t.Open.WithNullableLength(openOff, &openLen)
  338. }
  339. switch addr := f.Hub.Address.(type) {
  340. case *cdssdk.HttpAddressInfo:
  341. t.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: f.Hub})
  342. t.Env().Pinned = true
  343. case *cdssdk.GRPCAddressInfo:
  344. t.Env().ToEnvWorker(&ioswitch2.AgentWorker{Hub: f.Hub, Address: *addr})
  345. t.Env().Pinned = true
  346. default:
  347. return nil, fmt.Errorf("unsupported node address type %T", addr)
  348. }
  349. return t, nil
  350. case *ioswitch2.FromDriver:
  351. n := ctx.DAG.NewFromDriver(f, f.Handle)
  352. n.Env().ToEnvDriver()
  353. n.Env().Pinned = true
  354. if f.StreamIndex.IsRaw() {
  355. f.Handle.RangeHint.Offset = repRange.Offset
  356. f.Handle.RangeHint.Length = repRange.Length
  357. } else if f.StreamIndex.IsEC() {
  358. f.Handle.RangeHint.Offset = blkRange.Offset
  359. f.Handle.RangeHint.Length = blkRange.Length
  360. } else if f.StreamIndex.IsSegment() {
  361. segStart := ctx.Ft.SegmentParam.CalcSegmentStart(f.StreamIndex.Index)
  362. segLen := ctx.Ft.SegmentParam.Segments[f.StreamIndex.Index]
  363. segEnd := segStart + segLen
  364. // 打开的范围不超过本段的范围
  365. openOff := repRange.Offset - segStart
  366. openOff = math2.Clamp(openOff, 0, segLen)
  367. openLen := segLen
  368. if repRange.Length != nil {
  369. repEnd := repRange.Offset + *repRange.Length
  370. openEnd := math2.Min(repEnd, segEnd)
  371. openLen = openEnd - openOff
  372. }
  373. f.Handle.RangeHint.Offset = openOff
  374. f.Handle.RangeHint.Length = &openLen
  375. }
  376. return n, nil
  377. default:
  378. return nil, fmt.Errorf("unsupported from type %T", f)
  379. }
  380. }
  381. func buildToNode(ctx *ParseContext, t ioswitch2.To) (ops2.ToNode, error) {
  382. switch t := t.(type) {
  383. case *ioswitch2.ToShardStore:
  384. n := ctx.DAG.NewShardWrite(t, t.Storage.StorageID, t.FileHashStoreKey)
  385. if err := setEnvByAddress(n, t.Hub, t.Hub.Address); err != nil {
  386. return nil, err
  387. }
  388. n.Env().Pinned = true
  389. return n, nil
  390. case *ioswitch2.ToDriver:
  391. n := ctx.DAG.NewToDriver(t, t.Handle)
  392. n.Env().ToEnvDriver()
  393. n.Env().Pinned = true
  394. return n, nil
  395. case *ioswitch2.LoadToShared:
  396. n := ctx.DAG.NewSharedLoad(t, t.Storage.StorageID, t.UserID, t.PackageID, t.Path)
  397. if err := setEnvByAddress(n, t.Hub, t.Hub.Address); err != nil {
  398. return nil, err
  399. }
  400. n.Env().Pinned = true
  401. return n, nil
  402. default:
  403. return nil, fmt.Errorf("unsupported to type %T", t)
  404. }
  405. }
  406. func setEnvByAddress(n dag.Node, hub cdssdk.Hub, addr cdssdk.HubAddressInfo) error {
  407. switch addr := addr.(type) {
  408. case *cdssdk.HttpAddressInfo:
  409. n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: hub})
  410. case *cdssdk.GRPCAddressInfo:
  411. n.Env().ToEnvWorker(&ioswitch2.AgentWorker{Hub: hub, Address: *addr})
  412. default:
  413. return fmt.Errorf("unsupported node address type %T", addr)
  414. }
  415. return nil
  416. }
  417. // 根据StreamRange,调整SegmentSplit中分段的个数和每段的大小
  418. func fixSegmentSplit(ctx *ParseContext) error {
  419. var err error
  420. dag.WalkOnlyType[*ops2.SegmentSplitNode](ctx.DAG.Graph, func(node *ops2.SegmentSplitNode) bool {
  421. var strEnd *int64
  422. if ctx.StreamRange.Length != nil {
  423. e := ctx.StreamRange.Offset + *ctx.StreamRange.Length
  424. strEnd = &e
  425. }
  426. startSeg, endSeg := ctx.Ft.SegmentParam.CalcSegmentRange(ctx.StreamRange.Offset, strEnd)
  427. // 关闭超出范围的分段
  428. for i := 0; i < startSeg; i++ {
  429. node.Segments[i] = 0
  430. node.OutputStreams().Slots.Set(i, nil)
  431. }
  432. for i := endSeg; i < len(node.Segments); i++ {
  433. node.Segments[i] = 0
  434. node.OutputStreams().Slots.Set(i, nil)
  435. }
  436. // StreamRange开始的位置可能在某个分段的中间,此时这个分段的大小等于流开始位置到分段结束位置的距离
  437. startSegStart := ctx.Ft.SegmentParam.CalcSegmentStart(startSeg)
  438. node.Segments[startSeg] -= ctx.StreamRange.Offset - startSegStart
  439. // StreamRange结束的位置可能在某个分段的中间,此时这个分段的大小就等于流结束位置到分段起始位置的距离
  440. if strEnd != nil {
  441. endSegStart := ctx.Ft.SegmentParam.CalcSegmentStart(endSeg - 1)
  442. node.Segments[endSeg-1] = *strEnd - endSegStart
  443. }
  444. return true
  445. })
  446. return err
  447. }
  448. // 从SegmentJoin中删除未使用的分段
  449. func fixSegmentJoin(ctx *ParseContext) error {
  450. var err error
  451. dag.WalkOnlyType[*ops2.SegmentJoinNode](ctx.DAG.Graph, func(node *ops2.SegmentJoinNode) bool {
  452. start := ctx.StreamRange.Offset
  453. var end *int64
  454. if ctx.StreamRange.Length != nil {
  455. e := ctx.StreamRange.Offset + *ctx.StreamRange.Length
  456. end = &e
  457. }
  458. startSeg, endSeg := ctx.Ft.SegmentParam.CalcSegmentRange(start, end)
  459. // 关闭超出范围的分段
  460. for i := 0; i < startSeg; i++ {
  461. node.InputStreams().ClearInputAt(node, i)
  462. }
  463. for i := endSeg; i < node.InputStreams().Len(); i++ {
  464. node.InputStreams().ClearInputAt(node, i)
  465. }
  466. // 检查一下必须的分段是否都被加入到Join中
  467. for i := startSeg; i < endSeg; i++ {
  468. if node.InputStreams().Get(i) == nil {
  469. err = fmt.Errorf("segment %v missed to join an raw stream", i)
  470. return false
  471. }
  472. }
  473. return true
  474. })
  475. return err
  476. }
  477. // 删除未使用的SegmentJoin
  478. func removeUnusedSegmentJoin(ctx *ParseContext) bool {
  479. changed := false
  480. dag.WalkOnlyType[*ops2.SegmentJoinNode](ctx.DAG.Graph, func(node *ops2.SegmentJoinNode) bool {
  481. if node.Joined().Dst.Len() > 0 {
  482. return true
  483. }
  484. node.RemoveAllInputs()
  485. ctx.DAG.RemoveNode(node)
  486. return true
  487. })
  488. return changed
  489. }
  490. // 删除未使用的SegmentSplit
  491. func removeUnusedSegmentSplit(ctx *ParseContext) bool {
  492. changed := false
  493. dag.WalkOnlyType[*ops2.SegmentSplitNode](ctx.DAG.Graph, func(typ *ops2.SegmentSplitNode) bool {
  494. // Split出来的每一个流都没有被使用,才能删除这个指令
  495. for _, out := range typ.OutputStreams().Slots.RawArray() {
  496. if out.Dst.Len() > 0 {
  497. return true
  498. }
  499. }
  500. typ.RemoveAllStream()
  501. ctx.DAG.RemoveNode(typ)
  502. changed = true
  503. return true
  504. })
  505. return changed
  506. }
  507. // 如果Split的结果被完全用于Join,则省略Split和Join指令
  508. func omitSegmentSplitJoin(ctx *ParseContext) bool {
  509. changed := false
  510. dag.WalkOnlyType[*ops2.SegmentSplitNode](ctx.DAG.Graph, func(splitNode *ops2.SegmentSplitNode) bool {
  511. // Split指令的每一个输出都有且只有一个目的地
  512. var dstNode dag.Node
  513. for _, out := range splitNode.OutputStreams().Slots.RawArray() {
  514. if out.Dst.Len() != 1 {
  515. return true
  516. }
  517. if dstNode == nil {
  518. dstNode = out.Dst.Get(0)
  519. } else if dstNode != out.Dst.Get(0) {
  520. return true
  521. }
  522. }
  523. if dstNode == nil {
  524. return true
  525. }
  526. // 且这个目的地要是一个Join指令
  527. joinNode, ok := dstNode.(*ops2.SegmentJoinNode)
  528. if !ok {
  529. return true
  530. }
  531. // 同时这个Join指令的输入也必须全部来自Split指令的输出。
  532. // 由于上面判断了Split指令的输出目的地都相同,所以这里只要判断Join指令的输入数量是否与Split指令的输出数量相同即可
  533. if joinNode.InputStreams().Len() != splitNode.OutputStreams().Len() {
  534. return true
  535. }
  536. // 所有条件都满足,可以开始省略操作,将Join操作的目的地的输入流替换为Split操作的输入流:
  537. // F->Split->Join->T 变换为:F->T
  538. splitInput := splitNode.InputStreams().Get(0)
  539. for _, to := range joinNode.Joined().Dst.RawArray() {
  540. splitInput.To(to, to.InputStreams().IndexOf(joinNode.Joined()))
  541. }
  542. splitInput.NotTo(splitNode)
  543. // 并删除这两个指令
  544. ctx.DAG.RemoveNode(joinNode)
  545. ctx.DAG.RemoveNode(splitNode)
  546. changed = true
  547. return true
  548. })
  549. return changed
  550. }
  551. // 删除输出流未被使用的Join指令
  552. func removeUnusedJoin(ctx *ParseContext) bool {
  553. changed := false
  554. dag.WalkOnlyType[*ops2.ChunkedJoinNode](ctx.DAG.Graph, func(node *ops2.ChunkedJoinNode) bool {
  555. if node.Joined().Dst.Len() > 0 {
  556. return true
  557. }
  558. node.RemoveAllInputs()
  559. ctx.DAG.RemoveNode(node)
  560. return true
  561. })
  562. return changed
  563. }
  564. // 减少未使用的Multiply指令的输出流。如果减少到0,则删除该指令
  565. func removeUnusedMultiplyOutput(ctx *ParseContext) bool {
  566. changed := false
  567. dag.WalkOnlyType[*ops2.ECMultiplyNode](ctx.DAG.Graph, func(node *ops2.ECMultiplyNode) bool {
  568. outArr := node.OutputStreams().Slots.RawArray()
  569. for i2, out := range outArr {
  570. if out.Dst.Len() > 0 {
  571. continue
  572. }
  573. outArr[i2] = nil
  574. node.OutputIndexes[i2] = -2
  575. changed = true
  576. }
  577. node.OutputStreams().Slots.SetRawArray(lo2.RemoveAllDefault(outArr))
  578. node.OutputIndexes = lo2.RemoveAll(node.OutputIndexes, -2)
  579. // 如果所有输出流都被删除,则删除该指令
  580. if node.OutputStreams().Len() == 0 {
  581. node.RemoveAllInputs()
  582. ctx.DAG.RemoveNode(node)
  583. changed = true
  584. }
  585. return true
  586. })
  587. return changed
  588. }
  589. // 删除未使用的Split指令
  590. func removeUnusedSplit(ctx *ParseContext) bool {
  591. changed := false
  592. dag.WalkOnlyType[*ops2.ChunkedSplitNode](ctx.DAG.Graph, func(typ *ops2.ChunkedSplitNode) bool {
  593. // Split出来的每一个流都没有被使用,才能删除这个指令
  594. for _, out := range typ.OutputStreams().Slots.RawArray() {
  595. if out.Dst.Len() > 0 {
  596. return true
  597. }
  598. }
  599. typ.RemoveAllStream()
  600. ctx.DAG.RemoveNode(typ)
  601. changed = true
  602. return true
  603. })
  604. return changed
  605. }
  606. // 如果Split的结果被完全用于Join,则省略Split和Join指令
  607. func omitSplitJoin(ctx *ParseContext) bool {
  608. changed := false
  609. dag.WalkOnlyType[*ops2.ChunkedSplitNode](ctx.DAG.Graph, func(splitNode *ops2.ChunkedSplitNode) bool {
  610. // Split指令的每一个输出都有且只有一个目的地
  611. var dstNode dag.Node
  612. for _, out := range splitNode.OutputStreams().Slots.RawArray() {
  613. if out.Dst.Len() != 1 {
  614. return true
  615. }
  616. if dstNode == nil {
  617. dstNode = out.Dst.Get(0)
  618. } else if dstNode != out.Dst.Get(0) {
  619. return true
  620. }
  621. }
  622. if dstNode == nil {
  623. return true
  624. }
  625. // 且这个目的地要是一个Join指令
  626. joinNode, ok := dstNode.(*ops2.ChunkedJoinNode)
  627. if !ok {
  628. return true
  629. }
  630. // 同时这个Join指令的输入也必须全部来自Split指令的输出。
  631. // 由于上面判断了Split指令的输出目的地都相同,所以这里只要判断Join指令的输入数量是否与Split指令的输出数量相同即可
  632. if joinNode.InputStreams().Len() != splitNode.OutputStreams().Len() {
  633. return true
  634. }
  635. // 所有条件都满足,可以开始省略操作,将Join操作的目的地的输入流替换为Split操作的输入流:
  636. // F->Split->Join->T 变换为:F->T
  637. splitInput := splitNode.InputStreams().Get(0)
  638. for _, to := range joinNode.Joined().Dst.RawArray() {
  639. splitInput.To(to, to.InputStreams().IndexOf(joinNode.Joined()))
  640. }
  641. splitInput.NotTo(splitNode)
  642. // 并删除这两个指令
  643. ctx.DAG.RemoveNode(joinNode)
  644. ctx.DAG.RemoveNode(splitNode)
  645. changed = true
  646. return true
  647. })
  648. return changed
  649. }
  650. // 通过流的输入输出位置来确定指令的执行位置。
  651. // To系列的指令都会有固定的执行位置,这些位置会随着pin操作逐步扩散到整个DAG,
  652. // 所以理论上不会出现有指令的位置始终无法确定的情况。
  653. func pin(ctx *ParseContext) bool {
  654. changed := false
  655. ctx.DAG.Walk(func(node dag.Node) bool {
  656. if node.Env().Pinned {
  657. return true
  658. }
  659. var toEnv *dag.NodeEnv
  660. for _, out := range node.OutputStreams().Slots.RawArray() {
  661. for _, to := range out.Dst.RawArray() {
  662. if to.Env().Type == dag.EnvUnknown {
  663. continue
  664. }
  665. if toEnv == nil {
  666. toEnv = to.Env()
  667. } else if !toEnv.Equals(to.Env()) {
  668. toEnv = nil
  669. break
  670. }
  671. }
  672. }
  673. if toEnv != nil {
  674. if !node.Env().Equals(toEnv) {
  675. changed = true
  676. }
  677. *node.Env() = *toEnv
  678. return true
  679. }
  680. // 否则根据输入流的始发地来固定
  681. var fromEnv *dag.NodeEnv
  682. for _, in := range node.InputStreams().Slots.RawArray() {
  683. if in.Src.Env().Type == dag.EnvUnknown {
  684. continue
  685. }
  686. if fromEnv == nil {
  687. fromEnv = in.Src.Env()
  688. } else if !fromEnv.Equals(in.Src.Env()) {
  689. fromEnv = nil
  690. break
  691. }
  692. }
  693. if fromEnv != nil {
  694. if !node.Env().Equals(fromEnv) {
  695. changed = true
  696. }
  697. *node.Env() = *fromEnv
  698. }
  699. return true
  700. })
  701. return changed
  702. }
  703. // 删除未使用的From流,不会删除FromDriver
  704. func removeUnusedFromNode(ctx *ParseContext) {
  705. dag.WalkOnlyType[ops2.FromNode](ctx.DAG.Graph, func(node ops2.FromNode) bool {
  706. if _, ok := node.(*ops2.FromDriverNode); ok {
  707. return true
  708. }
  709. if node.Output().Var == nil {
  710. ctx.DAG.RemoveNode(node)
  711. }
  712. return true
  713. })
  714. }
  715. // 对于所有未使用的流,增加Drop指令
  716. func dropUnused(ctx *ParseContext) {
  717. ctx.DAG.Walk(func(node dag.Node) bool {
  718. for _, out := range node.OutputStreams().Slots.RawArray() {
  719. if out.Dst.Len() == 0 {
  720. n := ctx.DAG.NewDropStream()
  721. *n.Env() = *node.Env()
  722. n.SetInput(out)
  723. }
  724. }
  725. return true
  726. })
  727. }
  728. // 为IPFS写入指令存储结果
  729. func storeIPFSWriteResult(ctx *ParseContext) {
  730. dag.WalkOnlyType[*ops2.ShardWriteNode](ctx.DAG.Graph, func(n *ops2.ShardWriteNode) bool {
  731. if n.FileHashStoreKey == "" {
  732. return true
  733. }
  734. storeNode := ctx.DAG.NewStore()
  735. storeNode.Env().ToEnvDriver()
  736. storeNode.Store(n.FileHashStoreKey, n.FileHashVar())
  737. return true
  738. })
  739. }
  740. // 生成Range指令。StreamRange可能超过文件总大小,但Range指令会在数据量不够时不报错而是正常返回
  741. func generateRange(ctx *ParseContext) {
  742. for i := 0; i < len(ctx.Ft.Toes); i++ {
  743. to := ctx.Ft.Toes[i]
  744. toNode := ctx.ToNodes[to]
  745. toStrIdx := to.GetStreamIndex()
  746. toRng := to.GetRange()
  747. if toStrIdx.IsRaw() {
  748. n := ctx.DAG.NewRange()
  749. toInput := toNode.Input()
  750. *n.Env() = *toInput.Var.Src.Env()
  751. rnged := n.RangeStream(toInput.Var, exec.Range{
  752. Offset: toRng.Offset - ctx.StreamRange.Offset,
  753. Length: toRng.Length,
  754. })
  755. toInput.Var.NotTo(toNode)
  756. toNode.SetInput(rnged)
  757. } else if toStrIdx.IsEC() {
  758. stripSize := int64(ctx.Ft.ECParam.ChunkSize * ctx.Ft.ECParam.K)
  759. blkStartIdx := ctx.StreamRange.Offset / stripSize
  760. blkStart := blkStartIdx * int64(ctx.Ft.ECParam.ChunkSize)
  761. n := ctx.DAG.NewRange()
  762. toInput := toNode.Input()
  763. *n.Env() = *toInput.Var.Src.Env()
  764. rnged := n.RangeStream(toInput.Var, exec.Range{
  765. Offset: toRng.Offset - blkStart,
  766. Length: toRng.Length,
  767. })
  768. toInput.Var.NotTo(toNode)
  769. toNode.SetInput(rnged)
  770. } else if toStrIdx.IsSegment() {
  771. // if frNode, ok := toNode.Input().Var.From().Node.(ops2.FromNode); ok {
  772. // // 目前只有To也是分段时,才可能对接一个提供分段的From,此时不需要再生成Range指令
  773. // if frNode.GetFrom().GetStreamIndex().IsSegment() {
  774. // continue
  775. // }
  776. // }
  777. // segStart := ctx.Ft.SegmentParam.CalcSegmentStart(toStrIdx.Index)
  778. // strStart := segStart + toRng.Offset
  779. // n := ctx.DAG.NewRange()
  780. // toInput := toNode.Input()
  781. // *n.Env() = *toInput.Var.From().Node.Env()
  782. // rnged := n.RangeStream(toInput.Var, exec.Range{
  783. // Offset: strStart - ctx.StreamRange.Offset,
  784. // Length: toRng.Length,
  785. // })
  786. // toInput.Var.NotTo(toNode, toInput.Index)
  787. // toNode.SetInput(rnged)
  788. }
  789. }
  790. }
  791. // 生成Clone指令
  792. func generateClone(ctx *ParseContext) {
  793. ctx.DAG.Walk(func(node dag.Node) bool {
  794. for _, outVar := range node.OutputStreams().Slots.RawArray() {
  795. if outVar.Dst.Len() <= 1 {
  796. continue
  797. }
  798. c := ctx.DAG.NewCloneStream()
  799. *c.Env() = *node.Env()
  800. for _, to := range outVar.Dst.RawArray() {
  801. c.NewOutput().To(to, to.InputStreams().IndexOf(outVar))
  802. }
  803. outVar.Dst.Resize(0)
  804. c.SetInput(outVar)
  805. }
  806. for _, outVar := range node.OutputValues().Slots.RawArray() {
  807. if outVar.Dst.Len() <= 1 {
  808. continue
  809. }
  810. t := ctx.DAG.NewCloneValue()
  811. *t.Env() = *node.Env()
  812. for _, to := range outVar.Dst.RawArray() {
  813. t.NewOutput().To(to, to.InputValues().IndexOf(outVar))
  814. }
  815. outVar.Dst.Resize(0)
  816. t.SetInput(outVar)
  817. }
  818. return true
  819. })
  820. }

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