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.

check_package_redundancy.go 20 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. package event
  2. import (
  3. "context"
  4. "fmt"
  5. "strconv"
  6. "time"
  7. "github.com/samber/lo"
  8. "gitlink.org.cn/cloudream/common/pkgs/logger"
  9. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  10. "gitlink.org.cn/cloudream/common/utils/sort2"
  11. stgglb "gitlink.org.cn/cloudream/storage/common/globals"
  12. stgmod "gitlink.org.cn/cloudream/storage/common/models"
  13. "gitlink.org.cn/cloudream/storage/common/pkgs/distlock/reqbuilder"
  14. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch/plans"
  15. agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent"
  16. coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator"
  17. scevt "gitlink.org.cn/cloudream/storage/common/pkgs/mq/scanner/event"
  18. "gitlink.org.cn/cloudream/storage/scanner/internal/config"
  19. )
  20. const (
  21. monthHours = 30 * 24
  22. yearHours = 365 * 24
  23. )
  24. type CheckPackageRedundancy struct {
  25. *scevt.CheckPackageRedundancy
  26. }
  27. func NewCheckPackageRedundancy(evt *scevt.CheckPackageRedundancy) *CheckPackageRedundancy {
  28. return &CheckPackageRedundancy{
  29. CheckPackageRedundancy: evt,
  30. }
  31. }
  32. type NodeLoadInfo struct {
  33. Node cdssdk.Node
  34. LoadsRecentMonth int
  35. LoadsRecentYear int
  36. }
  37. func (t *CheckPackageRedundancy) TryMerge(other Event) bool {
  38. event, ok := other.(*CheckPackageRedundancy)
  39. if !ok {
  40. return false
  41. }
  42. return event.PackageID == t.PackageID
  43. }
  44. func (t *CheckPackageRedundancy) Execute(execCtx ExecuteContext) {
  45. log := logger.WithType[CheckPackageRedundancy]("Event")
  46. startTime := time.Now()
  47. log.Debugf("begin with %v", logger.FormatStruct(t.CheckPackageRedundancy))
  48. defer func() {
  49. log.Debugf("end, time: %v", time.Since(startTime))
  50. }()
  51. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  52. if err != nil {
  53. log.Warnf("new coordinator client: %s", err.Error())
  54. return
  55. }
  56. defer stgglb.CoordinatorMQPool.Release(coorCli)
  57. getObjs, err := coorCli.GetPackageObjectDetails(coormq.ReqGetPackageObjectDetails(t.PackageID))
  58. if err != nil {
  59. log.Warnf("getting package objects: %s", err.Error())
  60. return
  61. }
  62. getLogs, err := coorCli.GetPackageLoadLogDetails(coormq.ReqGetPackageLoadLogDetails(t.PackageID))
  63. if err != nil {
  64. log.Warnf("getting package load log details: %s", err.Error())
  65. return
  66. }
  67. // TODO UserID
  68. getNodes, err := coorCli.GetUserNodes(coormq.NewGetUserNodes(1))
  69. if err != nil {
  70. log.Warnf("getting all nodes: %s", err.Error())
  71. return
  72. }
  73. if len(getNodes.Nodes) == 0 {
  74. log.Warnf("no available nodes")
  75. return
  76. }
  77. allNodes := make(map[cdssdk.NodeID]*NodeLoadInfo)
  78. for _, node := range getNodes.Nodes {
  79. allNodes[node.NodeID] = &NodeLoadInfo{
  80. Node: node,
  81. }
  82. }
  83. for _, log := range getLogs.Logs {
  84. info, ok := allNodes[log.Storage.NodeID]
  85. if !ok {
  86. continue
  87. }
  88. sinceNow := time.Since(log.CreateTime)
  89. if sinceNow.Hours() < monthHours {
  90. info.LoadsRecentMonth++
  91. } else if sinceNow.Hours() < yearHours {
  92. info.LoadsRecentYear++
  93. }
  94. }
  95. var changedObjects []coormq.UpdatingObjectRedundancy
  96. defRep := cdssdk.DefaultRepRedundancy
  97. defEC := cdssdk.DefaultECRedundancy
  98. // TODO 目前rep的备份数量固定为2,所以这里直接选出两个节点
  99. mostBlockNodeIDs := t.summaryRepObjectBlockNodes(getObjs.Objects, 2)
  100. newRepNodes := t.chooseNewNodesForRep(&defRep, allNodes)
  101. rechoosedRepNodes := t.rechooseNodesForRep(mostBlockNodeIDs, &defRep, allNodes)
  102. newECNodes := t.chooseNewNodesForEC(&defEC, allNodes)
  103. // 加锁
  104. builder := reqbuilder.NewBuilder()
  105. for _, node := range newRepNodes {
  106. builder.IPFS().Buzy(node.Node.NodeID)
  107. }
  108. for _, node := range newECNodes {
  109. builder.IPFS().Buzy(node.Node.NodeID)
  110. }
  111. mutex, err := builder.MutexLock(execCtx.Args.DistLock)
  112. if err != nil {
  113. log.Warnf("acquiring dist lock: %s", err.Error())
  114. return
  115. }
  116. defer mutex.Unlock()
  117. for _, obj := range getObjs.Objects {
  118. var updating *coormq.UpdatingObjectRedundancy
  119. var err error
  120. shouldUseEC := obj.Object.Size > config.Cfg().ECFileSizeThreshold
  121. switch red := obj.Object.Redundancy.(type) {
  122. case *cdssdk.NoneRedundancy:
  123. if shouldUseEC {
  124. log.WithField("ObjectID", obj.Object.ObjectID).Debugf("redundancy: none -> ec")
  125. updating, err = t.noneToEC(obj, &defEC, newECNodes)
  126. } else {
  127. log.WithField("ObjectID", obj.Object.ObjectID).Debugf("redundancy: none -> rep")
  128. updating, err = t.noneToRep(obj, &defRep, newRepNodes)
  129. }
  130. case *cdssdk.RepRedundancy:
  131. if shouldUseEC {
  132. log.WithField("ObjectID", obj.Object.ObjectID).Debugf("redundancy: rep -> ec")
  133. updating, err = t.repToEC(obj, &defEC, newECNodes)
  134. } else {
  135. updating, err = t.repToRep(obj, &defRep, rechoosedRepNodes)
  136. }
  137. case *cdssdk.ECRedundancy:
  138. if shouldUseEC {
  139. uploadNodes := t.rechooseNodesForEC(obj, red, allNodes)
  140. updating, err = t.ecToEC(obj, red, &defEC, uploadNodes)
  141. } else {
  142. log.WithField("ObjectID", obj.Object.ObjectID).Debugf("redundancy: ec -> rep")
  143. updating, err = t.ecToRep(obj, red, &defRep, newRepNodes)
  144. }
  145. }
  146. if updating != nil {
  147. changedObjects = append(changedObjects, *updating)
  148. }
  149. if err != nil {
  150. log.WithField("ObjectID", obj.Object.ObjectID).Warnf("%s, its redundancy wont be changed", err.Error())
  151. }
  152. }
  153. if len(changedObjects) == 0 {
  154. return
  155. }
  156. _, err = coorCli.UpdateObjectRedundancy(coormq.ReqUpdateObjectRedundancy(changedObjects))
  157. if err != nil {
  158. log.Warnf("requesting to change object redundancy: %s", err.Error())
  159. return
  160. }
  161. }
  162. // 统计每个对象块所在的节点,选出块最多的不超过nodeCnt个节点
  163. func (t *CheckPackageRedundancy) summaryRepObjectBlockNodes(objs []stgmod.ObjectDetail, nodeCnt int) []cdssdk.NodeID {
  164. type nodeBlocks struct {
  165. NodeID cdssdk.NodeID
  166. Count int
  167. }
  168. nodeBlocksMap := make(map[cdssdk.NodeID]*nodeBlocks)
  169. for _, obj := range objs {
  170. shouldUseEC := obj.Object.Size > config.Cfg().ECFileSizeThreshold
  171. if _, ok := obj.Object.Redundancy.(*cdssdk.RepRedundancy); ok && !shouldUseEC {
  172. for _, block := range obj.Blocks {
  173. if _, ok := nodeBlocksMap[block.NodeID]; !ok {
  174. nodeBlocksMap[block.NodeID] = &nodeBlocks{
  175. NodeID: block.NodeID,
  176. Count: 0,
  177. }
  178. }
  179. nodeBlocksMap[block.NodeID].Count++
  180. }
  181. }
  182. }
  183. nodes := lo.Values(nodeBlocksMap)
  184. sort2.Sort(nodes, func(left *nodeBlocks, right *nodeBlocks) int {
  185. return right.Count - left.Count
  186. })
  187. ids := lo.Map(nodes, func(item *nodeBlocks, idx int) cdssdk.NodeID { return item.NodeID })
  188. if len(ids) > nodeCnt {
  189. ids = ids[:nodeCnt]
  190. }
  191. return ids
  192. }
  193. func (t *CheckPackageRedundancy) chooseNewNodesForRep(red *cdssdk.RepRedundancy, allNodes map[cdssdk.NodeID]*NodeLoadInfo) []*NodeLoadInfo {
  194. sortedNodes := sort2.Sort(lo.Values(allNodes), func(left *NodeLoadInfo, right *NodeLoadInfo) int {
  195. dm := right.LoadsRecentMonth - left.LoadsRecentMonth
  196. if dm != 0 {
  197. return dm
  198. }
  199. return right.LoadsRecentYear - left.LoadsRecentYear
  200. })
  201. return t.chooseSoManyNodes(red.RepCount, sortedNodes)
  202. }
  203. func (t *CheckPackageRedundancy) chooseNewNodesForEC(red *cdssdk.ECRedundancy, allNodes map[cdssdk.NodeID]*NodeLoadInfo) []*NodeLoadInfo {
  204. sortedNodes := sort2.Sort(lo.Values(allNodes), func(left *NodeLoadInfo, right *NodeLoadInfo) int {
  205. dm := right.LoadsRecentMonth - left.LoadsRecentMonth
  206. if dm != 0 {
  207. return dm
  208. }
  209. return right.LoadsRecentYear - left.LoadsRecentYear
  210. })
  211. return t.chooseSoManyNodes(red.N, sortedNodes)
  212. }
  213. func (t *CheckPackageRedundancy) rechooseNodesForRep(mostBlockNodeIDs []cdssdk.NodeID, red *cdssdk.RepRedundancy, allNodes map[cdssdk.NodeID]*NodeLoadInfo) []*NodeLoadInfo {
  214. type rechooseNode struct {
  215. *NodeLoadInfo
  216. HasBlock bool
  217. }
  218. var rechooseNodes []*rechooseNode
  219. for _, node := range allNodes {
  220. hasBlock := false
  221. for _, id := range mostBlockNodeIDs {
  222. if id == node.Node.NodeID {
  223. hasBlock = true
  224. break
  225. }
  226. }
  227. rechooseNodes = append(rechooseNodes, &rechooseNode{
  228. NodeLoadInfo: node,
  229. HasBlock: hasBlock,
  230. })
  231. }
  232. sortedNodes := sort2.Sort(rechooseNodes, func(left *rechooseNode, right *rechooseNode) int {
  233. dm := right.LoadsRecentMonth - left.LoadsRecentMonth
  234. if dm != 0 {
  235. return dm
  236. }
  237. // 已经缓存了文件块的节点优先选择
  238. v := sort2.CmpBool(right.HasBlock, left.HasBlock)
  239. if v != 0 {
  240. return v
  241. }
  242. return right.LoadsRecentYear - left.LoadsRecentYear
  243. })
  244. return t.chooseSoManyNodes(red.RepCount, lo.Map(sortedNodes, func(node *rechooseNode, idx int) *NodeLoadInfo { return node.NodeLoadInfo }))
  245. }
  246. func (t *CheckPackageRedundancy) rechooseNodesForEC(obj stgmod.ObjectDetail, red *cdssdk.ECRedundancy, allNodes map[cdssdk.NodeID]*NodeLoadInfo) []*NodeLoadInfo {
  247. type rechooseNode struct {
  248. *NodeLoadInfo
  249. CachedBlockIndex int
  250. }
  251. var rechooseNodes []*rechooseNode
  252. for _, node := range allNodes {
  253. cachedBlockIndex := -1
  254. for _, block := range obj.Blocks {
  255. if block.NodeID == node.Node.NodeID {
  256. cachedBlockIndex = block.Index
  257. break
  258. }
  259. }
  260. rechooseNodes = append(rechooseNodes, &rechooseNode{
  261. NodeLoadInfo: node,
  262. CachedBlockIndex: cachedBlockIndex,
  263. })
  264. }
  265. sortedNodes := sort2.Sort(rechooseNodes, func(left *rechooseNode, right *rechooseNode) int {
  266. dm := right.LoadsRecentMonth - left.LoadsRecentMonth
  267. if dm != 0 {
  268. return dm
  269. }
  270. // 已经缓存了文件块的节点优先选择
  271. v := sort2.CmpBool(right.CachedBlockIndex > -1, left.CachedBlockIndex > -1)
  272. if v != 0 {
  273. return v
  274. }
  275. return right.LoadsRecentYear - left.LoadsRecentYear
  276. })
  277. // TODO 可以考虑选择已有块的节点时,能依然按照Index顺序选择
  278. return t.chooseSoManyNodes(red.N, lo.Map(sortedNodes, func(node *rechooseNode, idx int) *NodeLoadInfo { return node.NodeLoadInfo }))
  279. }
  280. func (t *CheckPackageRedundancy) chooseSoManyNodes(count int, nodes []*NodeLoadInfo) []*NodeLoadInfo {
  281. repeateCount := (count + len(nodes) - 1) / len(nodes)
  282. extedNodes := make([]*NodeLoadInfo, repeateCount*len(nodes))
  283. // 使用复制的方式将节点数扩充到要求的数量
  284. // 复制之后的结构:ABCD -> AAABBBCCCDDD
  285. for p := 0; p < repeateCount; p++ {
  286. for i, node := range nodes {
  287. putIdx := i*repeateCount + p
  288. extedNodes[putIdx] = node
  289. }
  290. }
  291. extedNodes = extedNodes[:count]
  292. var chosen []*NodeLoadInfo
  293. for len(chosen) < count {
  294. // 在每一轮内都选不同地区的节点,如果节点数不够,那么就再来一轮
  295. chosenLocations := make(map[cdssdk.LocationID]bool)
  296. for i, node := range extedNodes {
  297. if node == nil {
  298. continue
  299. }
  300. if chosenLocations[node.Node.LocationID] {
  301. continue
  302. }
  303. chosen = append(chosen, node)
  304. chosenLocations[node.Node.LocationID] = true
  305. extedNodes[i] = nil
  306. }
  307. }
  308. return chosen
  309. }
  310. func (t *CheckPackageRedundancy) noneToRep(obj stgmod.ObjectDetail, red *cdssdk.RepRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.UpdatingObjectRedundancy, error) {
  311. if len(obj.Blocks) == 0 {
  312. return nil, fmt.Errorf("object is not cached on any nodes, cannot change its redundancy to rep")
  313. }
  314. // 如果选择的备份节点都是同一个,那么就只要上传一次
  315. uploadNodes = lo.UniqBy(uploadNodes, func(item *NodeLoadInfo) cdssdk.NodeID { return item.Node.NodeID })
  316. var blocks []stgmod.ObjectBlock
  317. for _, node := range uploadNodes {
  318. err := t.pinObject(node.Node.NodeID, obj.Object.FileHash)
  319. if err != nil {
  320. return nil, err
  321. }
  322. blocks = append(blocks, stgmod.ObjectBlock{
  323. ObjectID: obj.Object.ObjectID,
  324. Index: 0,
  325. NodeID: node.Node.NodeID,
  326. FileHash: obj.Object.FileHash,
  327. })
  328. }
  329. return &coormq.UpdatingObjectRedundancy{
  330. ObjectID: obj.Object.ObjectID,
  331. Redundancy: red,
  332. Blocks: blocks,
  333. }, nil
  334. }
  335. func (t *CheckPackageRedundancy) noneToEC(obj stgmod.ObjectDetail, red *cdssdk.ECRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.UpdatingObjectRedundancy, error) {
  336. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  337. if err != nil {
  338. return nil, fmt.Errorf("new coordinator client: %w", err)
  339. }
  340. defer stgglb.CoordinatorMQPool.Release(coorCli)
  341. if len(obj.Blocks) == 0 {
  342. return nil, fmt.Errorf("object is not cached on any nodes, cannot change its redundancy to ec")
  343. }
  344. getNodes, err := coorCli.GetNodes(coormq.NewGetNodes([]cdssdk.NodeID{obj.Blocks[0].NodeID}))
  345. if err != nil {
  346. return nil, fmt.Errorf("requesting to get nodes: %w", err)
  347. }
  348. ft := plans.NewFromTo()
  349. ft.AddFrom(plans.NewFromNode(obj.Object.FileHash, &getNodes.Nodes[0], -1))
  350. for i := 0; i < red.N; i++ {
  351. ft.AddTo(plans.NewToNode(uploadNodes[i].Node, i, fmt.Sprintf("%d", i)))
  352. }
  353. parser := plans.NewParser(*red)
  354. plans := plans.NewPlanBuilder()
  355. err = parser.Parse(ft, plans)
  356. if err != nil {
  357. return nil, fmt.Errorf("parsing plan: %w", err)
  358. }
  359. ioRet, err := plans.Execute().Wait(context.TODO())
  360. if err != nil {
  361. return nil, fmt.Errorf("executing io plan: %w", err)
  362. }
  363. var blocks []stgmod.ObjectBlock
  364. for i := 0; i < red.N; i++ {
  365. blocks = append(blocks, stgmod.ObjectBlock{
  366. ObjectID: obj.Object.ObjectID,
  367. Index: i,
  368. NodeID: uploadNodes[i].Node.NodeID,
  369. FileHash: ioRet[fmt.Sprintf("%d", i)].(string),
  370. })
  371. }
  372. return &coormq.UpdatingObjectRedundancy{
  373. ObjectID: obj.Object.ObjectID,
  374. Redundancy: red,
  375. Blocks: blocks,
  376. }, nil
  377. }
  378. func (t *CheckPackageRedundancy) repToRep(obj stgmod.ObjectDetail, red *cdssdk.RepRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.UpdatingObjectRedundancy, error) {
  379. if len(obj.Blocks) == 0 {
  380. return nil, fmt.Errorf("object is not cached on any nodes, cannot change its redundancy to rep")
  381. }
  382. // 如果选择的备份节点都是同一个,那么就只要上传一次
  383. uploadNodes = lo.UniqBy(uploadNodes, func(item *NodeLoadInfo) cdssdk.NodeID { return item.Node.NodeID })
  384. for _, node := range uploadNodes {
  385. err := t.pinObject(node.Node.NodeID, obj.Object.FileHash)
  386. if err != nil {
  387. logger.WithField("ObjectID", obj.Object.ObjectID).
  388. Warn(err.Error())
  389. return nil, err
  390. }
  391. }
  392. var blocks []stgmod.ObjectBlock
  393. for _, node := range uploadNodes {
  394. // 由于更新冗余方式会删除所有Block记录然后重新填充,
  395. // 所以即使是节点跳过了上传,也需要为它添加一条Block记录
  396. blocks = append(blocks, stgmod.ObjectBlock{
  397. ObjectID: obj.Object.ObjectID,
  398. Index: 0,
  399. NodeID: node.Node.NodeID,
  400. FileHash: obj.Object.FileHash,
  401. })
  402. }
  403. return &coormq.UpdatingObjectRedundancy{
  404. ObjectID: obj.Object.ObjectID,
  405. Redundancy: red,
  406. Blocks: blocks,
  407. }, nil
  408. }
  409. func (t *CheckPackageRedundancy) repToEC(obj stgmod.ObjectDetail, red *cdssdk.ECRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.UpdatingObjectRedundancy, error) {
  410. return t.noneToEC(obj, red, uploadNodes)
  411. }
  412. func (t *CheckPackageRedundancy) ecToRep(obj stgmod.ObjectDetail, srcRed *cdssdk.ECRedundancy, tarRed *cdssdk.RepRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.UpdatingObjectRedundancy, error) {
  413. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  414. if err != nil {
  415. return nil, fmt.Errorf("new coordinator client: %w", err)
  416. }
  417. defer stgglb.CoordinatorMQPool.Release(coorCli)
  418. var chosenBlocks []stgmod.GrouppedObjectBlock
  419. var chosenBlockIndexes []int
  420. for _, block := range obj.GroupBlocks() {
  421. if len(block.NodeIDs) > 0 {
  422. chosenBlocks = append(chosenBlocks, block)
  423. chosenBlockIndexes = append(chosenBlockIndexes, block.Index)
  424. }
  425. if len(chosenBlocks) == srcRed.K {
  426. break
  427. }
  428. }
  429. if len(chosenBlocks) < srcRed.K {
  430. return nil, fmt.Errorf("no enough blocks to reconstruct the original file data")
  431. }
  432. // 如果选择的备份节点都是同一个,那么就只要上传一次
  433. uploadNodes = lo.UniqBy(uploadNodes, func(item *NodeLoadInfo) cdssdk.NodeID { return item.Node.NodeID })
  434. // 每个被选节点都在自己节点上重建原始数据
  435. parser := plans.NewParser(*srcRed)
  436. planBlder := plans.NewPlanBuilder()
  437. for i := range uploadNodes {
  438. ft := plans.NewFromTo()
  439. for _, block := range chosenBlocks {
  440. ft.AddFrom(plans.NewFromNode(block.FileHash, &uploadNodes[i].Node, block.Index))
  441. }
  442. len := obj.Object.Size
  443. ft.AddTo(plans.NewToNodeWithRange(uploadNodes[i].Node, -1, fmt.Sprintf("%d", i), plans.Range{
  444. Offset: 0,
  445. Length: &len,
  446. }))
  447. err := parser.Parse(ft, planBlder)
  448. if err != nil {
  449. return nil, fmt.Errorf("parsing plan: %w", err)
  450. }
  451. }
  452. ioRet, err := planBlder.Execute().Wait(context.TODO())
  453. if err != nil {
  454. return nil, fmt.Errorf("executing io plan: %w", err)
  455. }
  456. var blocks []stgmod.ObjectBlock
  457. for i := range uploadNodes {
  458. blocks = append(blocks, stgmod.ObjectBlock{
  459. ObjectID: obj.Object.ObjectID,
  460. Index: 0,
  461. NodeID: uploadNodes[i].Node.NodeID,
  462. FileHash: ioRet[fmt.Sprintf("%d", i)].(string),
  463. })
  464. }
  465. return &coormq.UpdatingObjectRedundancy{
  466. ObjectID: obj.Object.ObjectID,
  467. Redundancy: tarRed,
  468. Blocks: blocks,
  469. }, nil
  470. }
  471. func (t *CheckPackageRedundancy) ecToEC(obj stgmod.ObjectDetail, srcRed *cdssdk.ECRedundancy, tarRed *cdssdk.ECRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.UpdatingObjectRedundancy, error) {
  472. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  473. if err != nil {
  474. return nil, fmt.Errorf("new coordinator client: %w", err)
  475. }
  476. defer stgglb.CoordinatorMQPool.Release(coorCli)
  477. grpBlocks := obj.GroupBlocks()
  478. var chosenBlocks []stgmod.GrouppedObjectBlock
  479. for _, block := range grpBlocks {
  480. if len(block.NodeIDs) > 0 {
  481. chosenBlocks = append(chosenBlocks, block)
  482. }
  483. if len(chosenBlocks) == srcRed.K {
  484. break
  485. }
  486. }
  487. if len(chosenBlocks) < srcRed.K {
  488. return nil, fmt.Errorf("no enough blocks to reconstruct the original file data")
  489. }
  490. // 目前EC的参数都相同,所以可以不用重建出完整数据然后再分块,可以直接构建出目的节点需要的块
  491. parser := plans.NewParser(*srcRed)
  492. planBlder := plans.NewPlanBuilder()
  493. var newBlocks []stgmod.ObjectBlock
  494. shouldUpdateBlocks := false
  495. for i, node := range uploadNodes {
  496. newBlock := stgmod.ObjectBlock{
  497. ObjectID: obj.Object.ObjectID,
  498. Index: i,
  499. NodeID: node.Node.NodeID,
  500. }
  501. grp, ok := lo.Find(grpBlocks, func(grp stgmod.GrouppedObjectBlock) bool { return grp.Index == i })
  502. // 如果新选中的节点已经记录在Block表中,那么就不需要任何变更
  503. if ok && lo.Contains(grp.NodeIDs, node.Node.NodeID) {
  504. newBlock.FileHash = grp.FileHash
  505. newBlocks = append(newBlocks, newBlock)
  506. continue
  507. }
  508. shouldUpdateBlocks = true
  509. // 否则就要重建出这个节点需要的块
  510. ft := plans.NewFromTo()
  511. for _, block := range chosenBlocks {
  512. ft.AddFrom(plans.NewFromNode(block.FileHash, &node.Node, block.Index))
  513. }
  514. // 输出只需要自己要保存的那一块
  515. ft.AddTo(plans.NewToNode(node.Node, i, fmt.Sprintf("%d", i)))
  516. err := parser.Parse(ft, planBlder)
  517. if err != nil {
  518. return nil, fmt.Errorf("parsing plan: %w", err)
  519. }
  520. newBlocks = append(newBlocks, newBlock)
  521. }
  522. // 如果没有任何Plan,Wait会直接返回成功
  523. ret, err := planBlder.Execute().Wait(context.TODO())
  524. if err != nil {
  525. return nil, fmt.Errorf("executing io plan: %w", err)
  526. }
  527. if !shouldUpdateBlocks {
  528. return nil, nil
  529. }
  530. for k, v := range ret {
  531. idx, err := strconv.ParseInt(k, 10, 64)
  532. if err != nil {
  533. return nil, fmt.Errorf("parsing result key %s as index: %w", k, err)
  534. }
  535. newBlocks[idx].FileHash = v.(string)
  536. }
  537. return &coormq.UpdatingObjectRedundancy{
  538. ObjectID: obj.Object.ObjectID,
  539. Redundancy: tarRed,
  540. Blocks: newBlocks,
  541. }, nil
  542. }
  543. func (t *CheckPackageRedundancy) pinObject(nodeID cdssdk.NodeID, fileHash string) error {
  544. agtCli, err := stgglb.AgentMQPool.Acquire(nodeID)
  545. if err != nil {
  546. return fmt.Errorf("new agent client: %w", err)
  547. }
  548. defer stgglb.AgentMQPool.Release(agtCli)
  549. _, err = agtCli.PinObject(agtmq.ReqPinObject([]string{fileHash}, false))
  550. if err != nil {
  551. return fmt.Errorf("start pinning object: %w", err)
  552. }
  553. return nil
  554. }
  555. func init() {
  556. RegisterMessageConvertor(NewCheckPackageRedundancy)
  557. }

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