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 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. package gen
  2. import (
  3. "fmt"
  4. "math"
  5. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag"
  6. "gitlink.org.cn/cloudream/common/utils/lo2"
  7. "gitlink.org.cn/cloudream/common/utils/math2"
  8. "gitlink.org.cn/cloudream/common/utils/os2"
  9. clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
  10. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2"
  11. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2"
  12. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser/state"
  13. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types"
  14. cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
  15. )
  16. // 检查使用不同编码时参数是否设置到位
  17. func CheckEncodingParams(ctx *state.GenerateState) error {
  18. for _, f := range ctx.Ft.Froms {
  19. if f.GetStreamIndex().IsEC() {
  20. ctx.UseEC = true
  21. if ctx.Ft.ECParam == nil {
  22. return fmt.Errorf("EC encoding parameters not set")
  23. }
  24. }
  25. if f.GetStreamIndex().IsSegment() {
  26. ctx.UseSegment = true
  27. if ctx.Ft.SegmentParam == nil {
  28. return fmt.Errorf("segment parameters not set")
  29. }
  30. }
  31. }
  32. for _, t := range ctx.Ft.Toes {
  33. if t.GetStreamIndex().IsEC() {
  34. ctx.UseEC = true
  35. if ctx.Ft.ECParam == nil {
  36. return fmt.Errorf("EC encoding parameters not set")
  37. }
  38. }
  39. if t.GetStreamIndex().IsSegment() {
  40. ctx.UseSegment = true
  41. if ctx.Ft.SegmentParam == nil {
  42. return fmt.Errorf("segment parameters not set")
  43. }
  44. }
  45. }
  46. return nil
  47. }
  48. // 计算输入流的打开范围。如果From或者To中包含EC的流,则会将打开范围扩大到条带大小的整数倍。
  49. func CalcStreamRange(ctx *state.GenerateState) {
  50. rng := math2.NewRange(math.MaxInt64, 0)
  51. for _, to := range ctx.Ft.Toes {
  52. strIdx := to.GetStreamIndex()
  53. if strIdx.IsRaw() {
  54. toRng := to.GetRange()
  55. rng.ExtendStart(toRng.Offset)
  56. if toRng.Length != nil {
  57. rng.ExtendEnd(toRng.Offset + *toRng.Length)
  58. } else {
  59. rng.Length = nil
  60. }
  61. } else if strIdx.IsEC() {
  62. toRng := to.GetRange()
  63. stripSize := ctx.Ft.ECParam.StripSize()
  64. blkStartIndex := math2.FloorDiv(toRng.Offset, int64(ctx.Ft.ECParam.ChunkSize))
  65. rng.ExtendStart(blkStartIndex * stripSize)
  66. if toRng.Length != nil {
  67. blkEndIndex := math2.CeilDiv(toRng.Offset+*toRng.Length, int64(ctx.Ft.ECParam.ChunkSize))
  68. rng.ExtendEnd(blkEndIndex * stripSize)
  69. } else {
  70. rng.Length = nil
  71. }
  72. } else if strIdx.IsSegment() {
  73. // Segment节点的Range是相对于本段的,需要加上本段的起始位置
  74. toRng := to.GetRange()
  75. segStart := ctx.Ft.SegmentParam.CalcSegmentStart(strIdx.Index)
  76. offset := toRng.Offset + segStart
  77. rng.ExtendStart(offset)
  78. if toRng.Length != nil {
  79. rng.ExtendEnd(offset + *toRng.Length)
  80. } else {
  81. rng.Length = nil
  82. }
  83. }
  84. }
  85. if ctx.UseEC {
  86. stripSize := ctx.Ft.ECParam.StripSize()
  87. rng.ExtendStart(math2.Floor(rng.Offset, stripSize))
  88. if rng.Length != nil {
  89. rng.ExtendEnd(math2.Ceil(rng.Offset+*rng.Length, stripSize))
  90. }
  91. }
  92. ctx.StreamRange = rng
  93. }
  94. func Extend(ctx *state.GenerateState) error {
  95. for _, fr := range ctx.Ft.Froms {
  96. frNode, err := buildFromNode(ctx, fr)
  97. if err != nil {
  98. return err
  99. }
  100. ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{
  101. Stream: frNode.Output().Var(),
  102. StreamIndex: fr.GetStreamIndex(),
  103. })
  104. // 对于完整文件的From,生成Split指令
  105. if fr.GetStreamIndex().IsRaw() {
  106. // 只有输入输出需要EC编码的块时,才生成相关指令
  107. if ctx.UseEC {
  108. splitNode := ctx.DAG.NewChunkedSplit(ctx.Ft.ECParam.ChunkSize, ctx.Ft.ECParam.K)
  109. splitNode.Split(frNode.Output().Var())
  110. for i := 0; i < ctx.Ft.ECParam.K; i++ {
  111. ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{
  112. Stream: splitNode.SubStream(i),
  113. StreamIndex: ioswitch2.ECStream(i),
  114. })
  115. }
  116. }
  117. // 同上
  118. if ctx.UseSegment {
  119. splitNode := ctx.DAG.NewSegmentSplit(ctx.Ft.SegmentParam.Segments)
  120. frNode.Output().Var().ToSlot(splitNode.InputSlot())
  121. for i := 0; i < len(ctx.Ft.SegmentParam.Segments); i++ {
  122. ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{
  123. Stream: splitNode.Segment(i),
  124. StreamIndex: ioswitch2.SegmentStream(i),
  125. })
  126. }
  127. }
  128. }
  129. }
  130. if ctx.UseEC {
  131. // 如果有K个不同的文件块流,则生成Multiply指令,同时针对其生成的流,生成Join指令
  132. ecInputStrs := make(map[int]*dag.StreamVar)
  133. for _, s := range ctx.IndexedStreams {
  134. if s.StreamIndex.IsEC() && ecInputStrs[s.StreamIndex.Index] == nil {
  135. ecInputStrs[s.StreamIndex.Index] = s.Stream
  136. if len(ecInputStrs) == ctx.Ft.ECParam.K {
  137. break
  138. }
  139. }
  140. }
  141. if len(ecInputStrs) == ctx.Ft.ECParam.K {
  142. mulNode := ctx.DAG.NewECMultiply(*ctx.Ft.ECParam)
  143. for i, s := range ecInputStrs {
  144. mulNode.AddInput(s, i)
  145. }
  146. for i := 0; i < ctx.Ft.ECParam.N; i++ {
  147. ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{
  148. Stream: mulNode.NewOutput(i),
  149. StreamIndex: ioswitch2.ECStream(i),
  150. })
  151. }
  152. joinNode := ctx.DAG.NewChunkedJoin(ctx.Ft.ECParam.ChunkSize)
  153. for i := 0; i < ctx.Ft.ECParam.K; i++ {
  154. // 不可能找不到流
  155. joinNode.AddInput(findOutputStream(ctx, ioswitch2.ECStream(i)))
  156. }
  157. ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{
  158. Stream: joinNode.Joined(),
  159. StreamIndex: ioswitch2.RawStream(),
  160. })
  161. }
  162. }
  163. if ctx.UseSegment {
  164. // 先假设有所有的顺序分段,生成Join指令,后续根据Range再实际计算是否缺少流
  165. joinNode := ctx.DAG.NewSegmentJoin(ctx.Ft.SegmentParam.Segments)
  166. for i := 0; i < ctx.Ft.SegmentParam.SegmentCount(); i++ {
  167. str := findOutputStream(ctx, ioswitch2.SegmentStream(i))
  168. if str != nil {
  169. str.ToSlot(joinNode.InputSlot(i))
  170. }
  171. }
  172. ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{
  173. Stream: joinNode.Joined(),
  174. StreamIndex: ioswitch2.RawStream(),
  175. })
  176. // SegmentJoin生成的Join指令可以用来生成EC块
  177. if ctx.UseEC {
  178. splitNode := ctx.DAG.NewChunkedSplit(ctx.Ft.ECParam.ChunkSize, ctx.Ft.ECParam.K)
  179. splitNode.Split(joinNode.Joined())
  180. mulNode := ctx.DAG.NewECMultiply(*ctx.Ft.ECParam)
  181. for i := 0; i < ctx.Ft.ECParam.K; i++ {
  182. mulNode.AddInput(splitNode.SubStream(i), i)
  183. ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{
  184. Stream: splitNode.SubStream(i),
  185. StreamIndex: ioswitch2.ECStream(i),
  186. })
  187. }
  188. for i := 0; i < ctx.Ft.ECParam.N; i++ {
  189. ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{
  190. Stream: mulNode.NewOutput(i),
  191. StreamIndex: ioswitch2.ECStream(i),
  192. })
  193. }
  194. }
  195. }
  196. // 为每一个To找到一个输入流
  197. for _, to := range ctx.Ft.Toes {
  198. toNode, err := buildToNode(ctx, to)
  199. if err != nil {
  200. return err
  201. }
  202. str := findOutputStream(ctx, to.GetStreamIndex())
  203. if str == nil {
  204. return fmt.Errorf("no output stream found for data index %d", to.GetStreamIndex())
  205. }
  206. str.ToSlot(toNode.Input())
  207. }
  208. return nil
  209. }
  210. func buildFromNode(ctx *state.GenerateState, f ioswitch2.From) (ops2.FromNode, error) {
  211. var repRange math2.Range
  212. repRange.Offset = ctx.StreamRange.Offset
  213. if ctx.StreamRange.Length != nil {
  214. repRngLen := *ctx.StreamRange.Length
  215. repRange.Length = &repRngLen
  216. }
  217. var blkRange math2.Range
  218. if ctx.UseEC {
  219. blkRange.Offset = ctx.StreamRange.Offset / int64(ctx.Ft.ECParam.ChunkSize*ctx.Ft.ECParam.K) * int64(ctx.Ft.ECParam.ChunkSize)
  220. if ctx.StreamRange.Length != nil {
  221. blkRngLen := *ctx.StreamRange.Length / int64(ctx.Ft.ECParam.ChunkSize*ctx.Ft.ECParam.K) * int64(ctx.Ft.ECParam.ChunkSize)
  222. blkRange.Length = &blkRngLen
  223. }
  224. }
  225. switch f := f.(type) {
  226. case *ioswitch2.FromShardStore:
  227. getShard := ctx.DAG.NewGetShardInfo(f.UserSpace, f.FileHash)
  228. getShard.Env().ToEnvDriver(true)
  229. read := ctx.DAG.NewBaseReadDyn(f, f.UserSpace, types.DefaultOpen())
  230. getShard.FileInfoVar().ToSlot(read.FileInfoSlot())
  231. if f.StreamIndex.IsRaw() {
  232. read.Option.WithNullableLength(repRange.Offset, repRange.Length)
  233. } else if f.StreamIndex.IsEC() {
  234. read.Option.WithNullableLength(blkRange.Offset, blkRange.Length)
  235. } else if f.StreamIndex.IsSegment() {
  236. segStart := ctx.Ft.SegmentParam.CalcSegmentStart(f.StreamIndex.Index)
  237. segLen := ctx.Ft.SegmentParam.Segments[f.StreamIndex.Index]
  238. segEnd := segStart + segLen
  239. // 打开的范围不超过本段的范围
  240. openOff := ctx.StreamRange.Offset - segStart
  241. openOff = math2.Clamp(openOff, 0, segLen)
  242. openLen := segLen
  243. if ctx.StreamRange.Length != nil {
  244. strEnd := ctx.StreamRange.Offset + *ctx.StreamRange.Length
  245. openEnd := math2.Min(strEnd, segEnd)
  246. openLen = openEnd - segStart - openOff
  247. }
  248. read.Option.WithNullableLength(openOff, &openLen)
  249. }
  250. if err := setEnvBySpace(read, &f.UserSpace); err != nil {
  251. return nil, fmt.Errorf("set node env by user space: %w", err)
  252. }
  253. return read, nil
  254. case *ioswitch2.FromDriver:
  255. n := ctx.DAG.NewFromDriver(f, f.Handle)
  256. n.Env().ToEnvDriver(true)
  257. if f.StreamIndex.IsRaw() {
  258. f.Handle.RangeHint.Offset = repRange.Offset
  259. f.Handle.RangeHint.Length = repRange.Length
  260. } else if f.StreamIndex.IsEC() {
  261. f.Handle.RangeHint.Offset = blkRange.Offset
  262. f.Handle.RangeHint.Length = blkRange.Length
  263. } else if f.StreamIndex.IsSegment() {
  264. segStart := ctx.Ft.SegmentParam.CalcSegmentStart(f.StreamIndex.Index)
  265. segLen := ctx.Ft.SegmentParam.Segments[f.StreamIndex.Index]
  266. segEnd := segStart + segLen
  267. // 打开的范围不超过本段的范围
  268. openOff := repRange.Offset - segStart
  269. openOff = math2.Clamp(openOff, 0, segLen)
  270. openLen := segLen
  271. if repRange.Length != nil {
  272. repEnd := repRange.Offset + *repRange.Length
  273. openEnd := math2.Min(repEnd, segEnd)
  274. openLen = openEnd - openOff
  275. }
  276. f.Handle.RangeHint.Offset = openOff
  277. f.Handle.RangeHint.Length = &openLen
  278. }
  279. return n, nil
  280. case *ioswitch2.FromBaseStore:
  281. // TODO 可以考虑支持设置读取范围
  282. n := ctx.DAG.NewBaseRead(f, f.UserSpace, f.Path, types.DefaultOpen())
  283. if err := setEnvBySpace(n, &f.UserSpace); err != nil {
  284. return nil, fmt.Errorf("set node env by user space: %w", err)
  285. }
  286. return n, nil
  287. default:
  288. return nil, fmt.Errorf("unsupported from type %T", f)
  289. }
  290. }
  291. func buildToNode(ctx *state.GenerateState, t ioswitch2.To) (ops2.ToNode, error) {
  292. switch t := t.(type) {
  293. case *ioswitch2.ToShardStore:
  294. tempFileName := types.MakeTempDirPath(&t.UserSpace, os2.GenerateRandomFileName(20))
  295. write := ctx.DAG.NewBaseWrite(t, t.UserSpace, tempFileName, types.WriteOption{})
  296. if err := setEnvBySpace(write, &t.UserSpace); err != nil {
  297. return nil, fmt.Errorf("set node env by user space: %w", err)
  298. }
  299. write.Env().Pinned = true
  300. add := ctx.DAG.NewStoreShard(t.UserSpace, t.ResultStoreKey)
  301. add.Env().ToEnvDriver(true)
  302. write.FileInfoVar().ToSlot(add.FileInfoSlot())
  303. return write, nil
  304. case *ioswitch2.ToDriver:
  305. n := ctx.DAG.NewToDriver(t, t.Handle)
  306. n.Env().ToEnvDriver(true)
  307. return n, nil
  308. case *ioswitch2.ToBaseStore:
  309. n := ctx.DAG.NewBaseWrite(t, t.UserSpace, t.ObjectPath, t.Option)
  310. if err := setEnvBySpace(n, &t.UserSpace); err != nil {
  311. return nil, fmt.Errorf("set node env by user space: %w", err)
  312. }
  313. n.Env().Pinned = true
  314. return n, nil
  315. default:
  316. return nil, fmt.Errorf("unsupported to type %T", t)
  317. }
  318. }
  319. func setEnvBySpace(n dag.Node, space *clitypes.UserSpaceDetail) error {
  320. if space.RecommendHub == nil {
  321. n.Env().ToEnvDriver(true)
  322. return nil
  323. }
  324. switch addr := space.RecommendHub.Address.(type) {
  325. case *cortypes.HttpAddressInfo:
  326. n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, true)
  327. case *cortypes.GRPCAddressInfo:
  328. n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, true)
  329. default:
  330. return fmt.Errorf("unsupported node address type %T", addr)
  331. }
  332. return nil
  333. }
  334. func findOutputStream(ctx *state.GenerateState, streamIndex ioswitch2.StreamIndex) *dag.StreamVar {
  335. var ret *dag.StreamVar
  336. for _, s := range ctx.IndexedStreams {
  337. if s.StreamIndex == streamIndex {
  338. ret = s.Stream
  339. break
  340. }
  341. }
  342. return ret
  343. }
  344. // 根据StreamRange,调整SegmentSplit中分段的个数和每段的大小
  345. func FixSegmentSplit(ctx *state.GenerateState) error {
  346. var err error
  347. dag.WalkOnlyType[*ops2.SegmentSplitNode](ctx.DAG.Graph, func(node *ops2.SegmentSplitNode) bool {
  348. var strEnd *int64
  349. if ctx.StreamRange.Length != nil {
  350. e := ctx.StreamRange.Offset + *ctx.StreamRange.Length
  351. strEnd = &e
  352. }
  353. startSeg, endSeg := ctx.Ft.SegmentParam.CalcSegmentRange(ctx.StreamRange.Offset, strEnd)
  354. // 关闭超出范围的分段
  355. for i := endSeg; i < len(node.Segments); i++ {
  356. node.OutputStreams().Get(i).ClearAllDst()
  357. }
  358. node.OutputStreams().Slots.RemoveRange(endSeg, ctx.Ft.SegmentParam.SegmentCount()-endSeg)
  359. node.Segments = lo2.RemoveRange(node.Segments, endSeg, ctx.Ft.SegmentParam.SegmentCount()-endSeg)
  360. for i := 0; i < startSeg; i++ {
  361. node.OutputStreams().Get(i).ClearAllDst()
  362. }
  363. node.OutputStreams().Slots.RemoveRange(0, startSeg)
  364. node.Segments = lo2.RemoveRange(node.Segments, 0, startSeg)
  365. // StreamRange开始的位置可能在某个分段的中间,此时这个分段的大小等于流开始位置到分段结束位置的距离
  366. startSegStart := ctx.Ft.SegmentParam.CalcSegmentStart(startSeg)
  367. node.Segments[0] -= ctx.StreamRange.Offset - startSegStart
  368. // StreamRange结束的位置可能在某个分段的中间,此时这个分段的大小就等于流结束位置到分段起始位置的距离
  369. if strEnd != nil {
  370. endSegStart := ctx.Ft.SegmentParam.CalcSegmentStart(endSeg - 1)
  371. node.Segments[len(node.Segments)-1] = *strEnd - endSegStart
  372. }
  373. return true
  374. })
  375. return err
  376. }
  377. // 从SegmentJoin中删除未使用的分段
  378. func FixSegmentJoin(ctx *state.GenerateState) error {
  379. var err error
  380. dag.WalkOnlyType[*ops2.SegmentJoinNode](ctx.DAG.Graph, func(node *ops2.SegmentJoinNode) bool {
  381. start := ctx.StreamRange.Offset
  382. var end *int64
  383. if ctx.StreamRange.Length != nil {
  384. e := ctx.StreamRange.Offset + *ctx.StreamRange.Length
  385. end = &e
  386. }
  387. startSeg, endSeg := ctx.Ft.SegmentParam.CalcSegmentRange(start, end)
  388. // 关闭超出范围的分段
  389. for i := endSeg; i < len(node.Segments); i++ {
  390. node.InputStreams().Get(i).NotTo(node)
  391. }
  392. node.InputStreams().Slots.RemoveRange(endSeg, ctx.Ft.SegmentParam.SegmentCount()-endSeg)
  393. node.Segments = lo2.RemoveRange(node.Segments, endSeg, ctx.Ft.SegmentParam.SegmentCount()-endSeg)
  394. for i := 0; i < startSeg; i++ {
  395. node.InputStreams().Get(i).NotTo(node)
  396. }
  397. node.InputStreams().Slots.RemoveRange(0, startSeg)
  398. node.Segments = lo2.RemoveRange(node.Segments, 0, startSeg)
  399. // StreamRange开始的位置可能在某个分段的中间,此时这个分段的大小等于流开始位置到分段结束位置的距离
  400. startSegStart := ctx.Ft.SegmentParam.CalcSegmentStart(startSeg)
  401. node.Segments[0] -= ctx.StreamRange.Offset - startSegStart
  402. // 检查一下必须的分段是否都被加入到Join中
  403. for i := 0; i < node.InputStreams().Len(); i++ {
  404. if node.InputStreams().Get(i) == nil {
  405. err = fmt.Errorf("segment %v missed to join an raw stream", i+startSeg)
  406. return false
  407. }
  408. }
  409. return true
  410. })
  411. return err
  412. }

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