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.

clean_pinned.go 27 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. package event
  2. import (
  3. "context"
  4. "fmt"
  5. "math"
  6. "math/rand"
  7. "sync"
  8. "time"
  9. "github.com/samber/lo"
  10. "gitlink.org.cn/cloudream/common/pkgs/bitmap"
  11. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
  12. "gitlink.org.cn/cloudream/common/pkgs/logger"
  13. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  14. "gitlink.org.cn/cloudream/common/utils/lo2"
  15. "gitlink.org.cn/cloudream/common/utils/math2"
  16. "gitlink.org.cn/cloudream/common/utils/sort2"
  17. "gitlink.org.cn/cloudream/storage/common/consts"
  18. stgglb "gitlink.org.cn/cloudream/storage/common/globals"
  19. stgmod "gitlink.org.cn/cloudream/storage/common/models"
  20. "gitlink.org.cn/cloudream/storage/common/pkgs/distlock/reqbuilder"
  21. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2"
  22. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser"
  23. agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent"
  24. coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator"
  25. scevt "gitlink.org.cn/cloudream/storage/common/pkgs/mq/scanner/event"
  26. )
  27. type CleanPinned struct {
  28. *scevt.CleanPinned
  29. }
  30. func NewCleanPinned(evt *scevt.CleanPinned) *CleanPinned {
  31. return &CleanPinned{
  32. CleanPinned: evt,
  33. }
  34. }
  35. func (t *CleanPinned) TryMerge(other Event) bool {
  36. event, ok := other.(*CleanPinned)
  37. if !ok {
  38. return false
  39. }
  40. return t.PackageID == event.PackageID
  41. }
  42. func (t *CleanPinned) Execute(execCtx ExecuteContext) {
  43. log := logger.WithType[CleanPinned]("Event")
  44. startTime := time.Now()
  45. log.Debugf("begin with %v", logger.FormatStruct(t.CleanPinned))
  46. defer func() {
  47. log.Debugf("end, time: %v", time.Since(startTime))
  48. }()
  49. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  50. if err != nil {
  51. log.Warnf("new coordinator client: %s", err.Error())
  52. return
  53. }
  54. defer stgglb.CoordinatorMQPool.Release(coorCli)
  55. getObjs, err := coorCli.GetPackageObjectDetails(coormq.ReqGetPackageObjectDetails(t.PackageID))
  56. if err != nil {
  57. log.Warnf("getting package objects: %s", err.Error())
  58. return
  59. }
  60. getLoadLog, err := coorCli.GetPackageLoadLogDetails(coormq.ReqGetPackageLoadLogDetails(t.PackageID))
  61. if err != nil {
  62. log.Warnf("getting package load log details: %s", err.Error())
  63. return
  64. }
  65. readerNodeIDs := lo.Map(getLoadLog.Logs, func(item coormq.PackageLoadLogDetail, idx int) cdssdk.NodeID { return item.Storage.NodeID })
  66. // 注意!需要保证allNodeID包含所有之后可能用到的节点ID
  67. // TOOD 可以考虑设计Cache机制
  68. var allNodeID []cdssdk.NodeID
  69. for _, obj := range getObjs.Objects {
  70. for _, block := range obj.Blocks {
  71. allNodeID = append(allNodeID, block.NodeID)
  72. }
  73. allNodeID = append(allNodeID, obj.PinnedAt...)
  74. }
  75. allNodeID = append(allNodeID, readerNodeIDs...)
  76. getNodeResp, err := coorCli.GetNodes(coormq.NewGetNodes(lo.Union(allNodeID)))
  77. if err != nil {
  78. log.Warnf("getting nodes: %s", err.Error())
  79. return
  80. }
  81. allNodeInfos := make(map[cdssdk.NodeID]*cdssdk.Node)
  82. for _, node := range getNodeResp.Nodes {
  83. n := node
  84. allNodeInfos[node.NodeID] = &n
  85. }
  86. // 只对ec和rep对象进行处理
  87. var ecObjects []stgmod.ObjectDetail
  88. var repObjects []stgmod.ObjectDetail
  89. for _, obj := range getObjs.Objects {
  90. if _, ok := obj.Object.Redundancy.(*cdssdk.ECRedundancy); ok {
  91. ecObjects = append(ecObjects, obj)
  92. } else if _, ok := obj.Object.Redundancy.(*cdssdk.RepRedundancy); ok {
  93. repObjects = append(repObjects, obj)
  94. }
  95. }
  96. planBld := exec.NewPlanBuilder()
  97. pinPlans := make(map[cdssdk.NodeID]*[]string)
  98. plnningNodeIDs := make(map[cdssdk.NodeID]bool)
  99. // 对于rep对象,统计出所有对象块分布最多的两个节点,用这两个节点代表所有rep对象块的分布,去进行退火算法
  100. var repObjectsUpdating []coormq.UpdatingObjectRedundancy
  101. repMostNodeIDs := t.summaryRepObjectBlockNodes(repObjects)
  102. solu := t.startAnnealing(allNodeInfos, readerNodeIDs, annealingObject{
  103. totalBlockCount: 1,
  104. minBlockCnt: 1,
  105. pinnedAt: repMostNodeIDs,
  106. blocks: nil,
  107. })
  108. for _, obj := range repObjects {
  109. repObjectsUpdating = append(repObjectsUpdating, t.makePlansForRepObject(solu, obj, pinPlans))
  110. }
  111. // 对于ec对象,则每个对象单独进行退火算法
  112. var ecObjectsUpdating []coormq.UpdatingObjectRedundancy
  113. for _, obj := range ecObjects {
  114. ecRed := obj.Object.Redundancy.(*cdssdk.ECRedundancy)
  115. solu := t.startAnnealing(allNodeInfos, readerNodeIDs, annealingObject{
  116. totalBlockCount: ecRed.N,
  117. minBlockCnt: ecRed.K,
  118. pinnedAt: obj.PinnedAt,
  119. blocks: obj.Blocks,
  120. })
  121. ecObjectsUpdating = append(ecObjectsUpdating, t.makePlansForECObject(allNodeInfos, solu, obj, planBld, plnningNodeIDs))
  122. }
  123. ioSwRets, err := t.executePlans(execCtx, pinPlans, planBld, plnningNodeIDs)
  124. if err != nil {
  125. log.Warn(err.Error())
  126. return
  127. }
  128. // 根据按照方案进行调整的结果,填充更新元数据的命令
  129. for i := range ecObjectsUpdating {
  130. t.populateECObjectEntry(&ecObjectsUpdating[i], ecObjects[i], ioSwRets)
  131. }
  132. finalEntries := append(repObjectsUpdating, ecObjectsUpdating...)
  133. if len(finalEntries) > 0 {
  134. _, err = coorCli.UpdateObjectRedundancy(coormq.ReqUpdateObjectRedundancy(finalEntries))
  135. if err != nil {
  136. log.Warnf("changing object redundancy: %s", err.Error())
  137. return
  138. }
  139. }
  140. }
  141. func (t *CleanPinned) summaryRepObjectBlockNodes(objs []stgmod.ObjectDetail) []cdssdk.NodeID {
  142. type nodeBlocks struct {
  143. NodeID cdssdk.NodeID
  144. Count int
  145. }
  146. nodeBlocksMap := make(map[cdssdk.NodeID]*nodeBlocks)
  147. for _, obj := range objs {
  148. cacheBlockNodes := make(map[cdssdk.NodeID]bool)
  149. for _, block := range obj.Blocks {
  150. if _, ok := nodeBlocksMap[block.NodeID]; !ok {
  151. nodeBlocksMap[block.NodeID] = &nodeBlocks{
  152. NodeID: block.NodeID,
  153. Count: 0,
  154. }
  155. }
  156. nodeBlocksMap[block.NodeID].Count++
  157. cacheBlockNodes[block.NodeID] = true
  158. }
  159. for _, nodeID := range obj.PinnedAt {
  160. if cacheBlockNodes[nodeID] {
  161. continue
  162. }
  163. if _, ok := nodeBlocksMap[nodeID]; !ok {
  164. nodeBlocksMap[nodeID] = &nodeBlocks{
  165. NodeID: nodeID,
  166. Count: 0,
  167. }
  168. }
  169. nodeBlocksMap[nodeID].Count++
  170. }
  171. }
  172. nodes := lo.Values(nodeBlocksMap)
  173. sort2.Sort(nodes, func(left *nodeBlocks, right *nodeBlocks) int {
  174. return right.Count - left.Count
  175. })
  176. // 只选出块数超过一半的节点,但要保证至少有两个节点
  177. for i := 2; i < len(nodes); i++ {
  178. if nodes[i].Count < len(objs)/2 {
  179. nodes = nodes[:i]
  180. break
  181. }
  182. }
  183. return lo.Map(nodes, func(item *nodeBlocks, idx int) cdssdk.NodeID { return item.NodeID })
  184. }
  185. type annealingState struct {
  186. allNodeInfos map[cdssdk.NodeID]*cdssdk.Node // 所有节点的信息
  187. readerNodeIDs []cdssdk.NodeID // 近期可能访问此对象的节点
  188. nodesSortedByReader map[cdssdk.NodeID][]nodeDist // 拥有数据的节点到每个可能访问对象的节点按距离排序
  189. object annealingObject // 进行退火的对象
  190. blockList []objectBlock // 排序后的块分布情况
  191. nodeBlockBitmaps map[cdssdk.NodeID]*bitmap.Bitmap64 // 用位图的形式表示每一个节点上有哪些块
  192. nodeCombTree combinatorialTree // 节点组合树,用于加速计算容灾度
  193. maxScore float64 // 搜索过程中得到过的最大分数
  194. maxScoreRmBlocks []bool // 最大分数对应的删除方案
  195. rmBlocks []bool // 当前删除方案
  196. inversedIndex int // 当前删除方案是从上一次的方案改动哪个flag而来的
  197. lastScore float64 // 上一次方案的分数
  198. }
  199. type objectBlock struct {
  200. Index int
  201. NodeID cdssdk.NodeID
  202. HasEntity bool // 节点拥有实际的文件数据块
  203. HasShadow bool // 如果节点拥有完整文件数据,那么认为这个节点拥有所有块,这些块被称为影子块
  204. FileHash string // 只有在拥有实际文件数据块时,这个字段才有值
  205. }
  206. type nodeDist struct {
  207. NodeID cdssdk.NodeID
  208. Distance float64
  209. }
  210. type combinatorialTree struct {
  211. nodes []combinatorialTreeNode
  212. blocksMaps map[int]bitmap.Bitmap64
  213. nodeIDToLocalNodeID map[cdssdk.NodeID]int
  214. localNodeIDToNodeID []cdssdk.NodeID
  215. }
  216. type annealingObject struct {
  217. totalBlockCount int
  218. minBlockCnt int
  219. pinnedAt []cdssdk.NodeID
  220. blocks []stgmod.ObjectBlock
  221. }
  222. const (
  223. iterActionNone = 0
  224. iterActionSkip = 1
  225. iterActionBreak = 2
  226. )
  227. func newCombinatorialTree(nodeBlocksMaps map[cdssdk.NodeID]*bitmap.Bitmap64) combinatorialTree {
  228. tree := combinatorialTree{
  229. blocksMaps: make(map[int]bitmap.Bitmap64),
  230. nodeIDToLocalNodeID: make(map[cdssdk.NodeID]int),
  231. }
  232. tree.nodes = make([]combinatorialTreeNode, (1 << len(nodeBlocksMaps)))
  233. for id, mp := range nodeBlocksMaps {
  234. tree.nodeIDToLocalNodeID[id] = len(tree.localNodeIDToNodeID)
  235. tree.blocksMaps[len(tree.localNodeIDToNodeID)] = *mp
  236. tree.localNodeIDToNodeID = append(tree.localNodeIDToNodeID, id)
  237. }
  238. tree.nodes[0].localNodeID = -1
  239. index := 1
  240. tree.initNode(0, &tree.nodes[0], &index)
  241. return tree
  242. }
  243. func (t *combinatorialTree) initNode(minAvaiLocalNodeID int, parent *combinatorialTreeNode, index *int) {
  244. for i := minAvaiLocalNodeID; i < len(t.nodeIDToLocalNodeID); i++ {
  245. curIndex := *index
  246. *index++
  247. bitMp := t.blocksMaps[i]
  248. bitMp.Or(&parent.blocksBitmap)
  249. t.nodes[curIndex] = combinatorialTreeNode{
  250. localNodeID: i,
  251. parent: parent,
  252. blocksBitmap: bitMp,
  253. }
  254. t.initNode(i+1, &t.nodes[curIndex], index)
  255. }
  256. }
  257. // 获得索引指定的节点所在的层
  258. func (t *combinatorialTree) GetDepth(index int) int {
  259. depth := 0
  260. // 反复判断节点在哪个子树。从左到右,子树节点的数量呈现8 4 2的变化,由此可以得到每个子树的索引值的范围
  261. subTreeCount := 1 << len(t.nodeIDToLocalNodeID)
  262. for index > 0 {
  263. if index < subTreeCount {
  264. // 定位到一个子树后,深度+1,然后进入这个子树,使用同样的方法再进行定位。
  265. // 进入子树后需要将索引值-1,因为要去掉子树的根节点
  266. index--
  267. depth++
  268. } else {
  269. // 如果索引值不在这个子树范围内,则将值减去子树的节点数量,
  270. // 这样每一次都可以视为使用同样的逻辑对不同大小的树进行判断。
  271. index -= subTreeCount
  272. }
  273. subTreeCount >>= 1
  274. }
  275. return depth
  276. }
  277. // 更新某一个算力中心节点的块分布位图,同时更新它对应组合树节点的所有子节点。
  278. // 如果更新到某个节点时,已有K个块,那么就不会再更新它的子节点
  279. func (t *combinatorialTree) UpdateBitmap(nodeID cdssdk.NodeID, mp bitmap.Bitmap64, k int) {
  280. t.blocksMaps[t.nodeIDToLocalNodeID[nodeID]] = mp
  281. // 首先定义两种遍历树节点时的移动方式:
  282. // 1. 竖直移动(深度增加):从一个节点移动到它最左边的子节点。每移动一步,index+1
  283. // 2. 水平移动:从一个节点移动到它右边的兄弟节点。每移动一步,根据它所在的深度,index+8,+4,+2
  284. // LocalNodeID从0开始,将其+1后得到移动步数steps。
  285. // 将移动步数拆成多部分,分配到上述的两种移动方式上,并进行任意组合,且保证第一次为至少进行一次的竖直移动,移动之后的节点都会是同一个计算中心节点。
  286. steps := t.nodeIDToLocalNodeID[nodeID] + 1
  287. for d := 1; d <= steps; d++ {
  288. t.iterCombBits(len(t.nodeIDToLocalNodeID)-1, steps-d, 0, func(i int) {
  289. index := d + i
  290. node := &t.nodes[index]
  291. newMp := t.blocksMaps[node.localNodeID]
  292. newMp.Or(&node.parent.blocksBitmap)
  293. node.blocksBitmap = newMp
  294. if newMp.Weight() >= k {
  295. return
  296. }
  297. t.iterChildren(index, func(index, parentIndex, depth int) int {
  298. curNode := &t.nodes[index]
  299. parentNode := t.nodes[parentIndex]
  300. newMp := t.blocksMaps[curNode.localNodeID]
  301. newMp.Or(&parentNode.blocksBitmap)
  302. curNode.blocksBitmap = newMp
  303. if newMp.Weight() >= k {
  304. return iterActionSkip
  305. }
  306. return iterActionNone
  307. })
  308. })
  309. }
  310. }
  311. // 遍历树,找到至少拥有K个块的树节点的最大深度
  312. func (t *combinatorialTree) FindKBlocksMaxDepth(k int) int {
  313. maxDepth := -1
  314. t.iterChildren(0, func(index, parentIndex, depth int) int {
  315. if t.nodes[index].blocksBitmap.Weight() >= k {
  316. if maxDepth < depth {
  317. maxDepth = depth
  318. }
  319. return iterActionSkip
  320. }
  321. // 如果到了叶子节点,还没有找到K个块,那就认为要满足K个块,至少需要再多一个节点,即深度+1。
  322. // 由于遍历时采用的是深度优先的算法,因此遍历到这个叶子节点时,叶子节点再加一个节点的组合已经在前面搜索过,
  323. // 所以用当前叶子节点深度+1来作为当前分支的结果就可以,即使当前情况下增加任意一个节点依然不够K块,
  324. // 可以使用同样的思路去递推到当前叶子节点增加两个块的情况。
  325. if t.nodes[index].localNodeID == len(t.nodeIDToLocalNodeID)-1 {
  326. if maxDepth < depth+1 {
  327. maxDepth = depth + 1
  328. }
  329. }
  330. return iterActionNone
  331. })
  332. if maxDepth == -1 || maxDepth > len(t.nodeIDToLocalNodeID) {
  333. return len(t.nodeIDToLocalNodeID)
  334. }
  335. return maxDepth
  336. }
  337. func (t *combinatorialTree) iterCombBits(width int, count int, offset int, callback func(int)) {
  338. if count == 0 {
  339. callback(offset)
  340. return
  341. }
  342. for b := width; b >= count; b-- {
  343. t.iterCombBits(b-1, count-1, offset+(1<<b), callback)
  344. }
  345. }
  346. func (t *combinatorialTree) iterChildren(index int, do func(index int, parentIndex int, depth int) int) {
  347. curNode := &t.nodes[index]
  348. childIndex := index + 1
  349. curDepth := t.GetDepth(index)
  350. childCounts := len(t.nodeIDToLocalNodeID) - 1 - curNode.localNodeID
  351. if childCounts == 0 {
  352. return
  353. }
  354. childTreeNodeCnt := 1 << (childCounts - 1)
  355. for c := 0; c < childCounts; c++ {
  356. act := t.itering(childIndex, index, curDepth+1, do)
  357. if act == iterActionBreak {
  358. return
  359. }
  360. childIndex += childTreeNodeCnt
  361. childTreeNodeCnt >>= 1
  362. }
  363. }
  364. func (t *combinatorialTree) itering(index int, parentIndex int, depth int, do func(index int, parentIndex int, depth int) int) int {
  365. act := do(index, parentIndex, depth)
  366. if act == iterActionBreak {
  367. return act
  368. }
  369. if act == iterActionSkip {
  370. return iterActionNone
  371. }
  372. curNode := &t.nodes[index]
  373. childIndex := index + 1
  374. childCounts := len(t.nodeIDToLocalNodeID) - 1 - curNode.localNodeID
  375. if childCounts == 0 {
  376. return iterActionNone
  377. }
  378. childTreeNodeCnt := 1 << (childCounts - 1)
  379. for c := 0; c < childCounts; c++ {
  380. act = t.itering(childIndex, index, depth+1, do)
  381. if act == iterActionBreak {
  382. return act
  383. }
  384. childIndex += childTreeNodeCnt
  385. childTreeNodeCnt >>= 1
  386. }
  387. return iterActionNone
  388. }
  389. type combinatorialTreeNode struct {
  390. localNodeID int
  391. parent *combinatorialTreeNode
  392. blocksBitmap bitmap.Bitmap64 // 选择了这个中心之后,所有中心一共包含多少种块
  393. }
  394. type annealingSolution struct {
  395. blockList []objectBlock // 所有节点的块分布情况
  396. rmBlocks []bool // 要删除哪些块
  397. }
  398. func (t *CleanPinned) startAnnealing(allNodeInfos map[cdssdk.NodeID]*cdssdk.Node, readerNodeIDs []cdssdk.NodeID, object annealingObject) annealingSolution {
  399. state := &annealingState{
  400. allNodeInfos: allNodeInfos,
  401. readerNodeIDs: readerNodeIDs,
  402. nodesSortedByReader: make(map[cdssdk.NodeID][]nodeDist),
  403. object: object,
  404. nodeBlockBitmaps: make(map[cdssdk.NodeID]*bitmap.Bitmap64),
  405. }
  406. t.initBlockList(state)
  407. if state.blockList == nil {
  408. return annealingSolution{}
  409. }
  410. t.initNodeBlockBitmap(state)
  411. t.sortNodeByReaderDistance(state)
  412. state.rmBlocks = make([]bool, len(state.blockList))
  413. state.inversedIndex = -1
  414. state.nodeCombTree = newCombinatorialTree(state.nodeBlockBitmaps)
  415. state.lastScore = t.calcScore(state)
  416. state.maxScore = state.lastScore
  417. state.maxScoreRmBlocks = lo2.ArrayClone(state.rmBlocks)
  418. // 模拟退火算法的温度
  419. curTemp := state.lastScore
  420. // 结束温度
  421. finalTemp := curTemp * 0.2
  422. // 冷却率
  423. coolingRate := 0.95
  424. for curTemp > finalTemp {
  425. state.inversedIndex = rand.Intn(len(state.rmBlocks))
  426. block := state.blockList[state.inversedIndex]
  427. state.rmBlocks[state.inversedIndex] = !state.rmBlocks[state.inversedIndex]
  428. state.nodeBlockBitmaps[block.NodeID].Set(block.Index, !state.rmBlocks[state.inversedIndex])
  429. state.nodeCombTree.UpdateBitmap(block.NodeID, *state.nodeBlockBitmaps[block.NodeID], state.object.minBlockCnt)
  430. curScore := t.calcScore(state)
  431. dScore := curScore - state.lastScore
  432. // 如果新方案比旧方案得分低,且没有要求强制接受新方案,那么就将变化改回去
  433. if curScore == 0 || (dScore < 0 && !t.alwaysAccept(curTemp, dScore, coolingRate)) {
  434. state.rmBlocks[state.inversedIndex] = !state.rmBlocks[state.inversedIndex]
  435. state.nodeBlockBitmaps[block.NodeID].Set(block.Index, !state.rmBlocks[state.inversedIndex])
  436. state.nodeCombTree.UpdateBitmap(block.NodeID, *state.nodeBlockBitmaps[block.NodeID], state.object.minBlockCnt)
  437. // fmt.Printf("\n")
  438. } else {
  439. // fmt.Printf(" accept!\n")
  440. state.lastScore = curScore
  441. if state.maxScore < curScore {
  442. state.maxScore = state.lastScore
  443. state.maxScoreRmBlocks = lo2.ArrayClone(state.rmBlocks)
  444. }
  445. }
  446. curTemp *= coolingRate
  447. }
  448. // fmt.Printf("final: %v\n", state.maxScoreRmBlocks)
  449. return annealingSolution{
  450. blockList: state.blockList,
  451. rmBlocks: state.maxScoreRmBlocks,
  452. }
  453. }
  454. func (t *CleanPinned) initBlockList(ctx *annealingState) {
  455. blocksMap := make(map[cdssdk.NodeID][]objectBlock)
  456. // 先生成所有的影子块
  457. for _, pinned := range ctx.object.pinnedAt {
  458. blocks := make([]objectBlock, 0, ctx.object.totalBlockCount)
  459. for i := 0; i < ctx.object.totalBlockCount; i++ {
  460. blocks = append(blocks, objectBlock{
  461. Index: i,
  462. NodeID: pinned,
  463. HasShadow: true,
  464. })
  465. }
  466. blocksMap[pinned] = blocks
  467. }
  468. // 再填充实际块
  469. for _, b := range ctx.object.blocks {
  470. blocks := blocksMap[b.NodeID]
  471. has := false
  472. for i := range blocks {
  473. if blocks[i].Index == b.Index {
  474. blocks[i].HasEntity = true
  475. blocks[i].FileHash = b.FileHash
  476. has = true
  477. break
  478. }
  479. }
  480. if has {
  481. continue
  482. }
  483. blocks = append(blocks, objectBlock{
  484. Index: b.Index,
  485. NodeID: b.NodeID,
  486. HasEntity: true,
  487. FileHash: b.FileHash,
  488. })
  489. blocksMap[b.NodeID] = blocks
  490. }
  491. var sortedBlocks []objectBlock
  492. for _, bs := range blocksMap {
  493. sortedBlocks = append(sortedBlocks, bs...)
  494. }
  495. sortedBlocks = sort2.Sort(sortedBlocks, func(left objectBlock, right objectBlock) int {
  496. d := left.NodeID - right.NodeID
  497. if d != 0 {
  498. return int(d)
  499. }
  500. return left.Index - right.Index
  501. })
  502. ctx.blockList = sortedBlocks
  503. }
  504. func (t *CleanPinned) initNodeBlockBitmap(state *annealingState) {
  505. for _, b := range state.blockList {
  506. mp, ok := state.nodeBlockBitmaps[b.NodeID]
  507. if !ok {
  508. nb := bitmap.Bitmap64(0)
  509. mp = &nb
  510. state.nodeBlockBitmaps[b.NodeID] = mp
  511. }
  512. mp.Set(b.Index, true)
  513. }
  514. }
  515. func (t *CleanPinned) sortNodeByReaderDistance(state *annealingState) {
  516. for _, r := range state.readerNodeIDs {
  517. var nodeDists []nodeDist
  518. for n := range state.nodeBlockBitmaps {
  519. if r == n {
  520. // 同节点时距离视为0.1
  521. nodeDists = append(nodeDists, nodeDist{
  522. NodeID: n,
  523. Distance: consts.NodeDistanceSameNode,
  524. })
  525. } else if state.allNodeInfos[r].LocationID == state.allNodeInfos[n].LocationID {
  526. // 同地区时距离视为1
  527. nodeDists = append(nodeDists, nodeDist{
  528. NodeID: n,
  529. Distance: consts.NodeDistanceSameLocation,
  530. })
  531. } else {
  532. // 不同地区时距离视为5
  533. nodeDists = append(nodeDists, nodeDist{
  534. NodeID: n,
  535. Distance: consts.NodeDistanceOther,
  536. })
  537. }
  538. }
  539. state.nodesSortedByReader[r] = sort2.Sort(nodeDists, func(left, right nodeDist) int { return sort2.Cmp(left.Distance, right.Distance) })
  540. }
  541. }
  542. func (t *CleanPinned) calcScore(state *annealingState) float64 {
  543. dt := t.calcDisasterTolerance(state)
  544. ac := t.calcMinAccessCost(state)
  545. sc := t.calcSpaceCost(state)
  546. dtSc := 1.0
  547. if dt < 1 {
  548. dtSc = 0
  549. } else if dt >= 2 {
  550. dtSc = 1.5
  551. }
  552. newSc := 0.0
  553. if dt == 0 || ac == 0 {
  554. newSc = 0
  555. } else {
  556. newSc = dtSc / (sc * ac)
  557. }
  558. // fmt.Printf("solu: %v, cur: %v, dt: %v, ac: %v, sc: %v \n", state.rmBlocks, newSc, dt, ac, sc)
  559. return newSc
  560. }
  561. // 计算容灾度
  562. func (t *CleanPinned) calcDisasterTolerance(state *annealingState) float64 {
  563. if state.inversedIndex != -1 {
  564. node := state.blockList[state.inversedIndex]
  565. state.nodeCombTree.UpdateBitmap(node.NodeID, *state.nodeBlockBitmaps[node.NodeID], state.object.minBlockCnt)
  566. }
  567. return float64(len(state.nodeBlockBitmaps) - state.nodeCombTree.FindKBlocksMaxDepth(state.object.minBlockCnt))
  568. }
  569. // 计算最小访问数据的代价
  570. func (t *CleanPinned) calcMinAccessCost(state *annealingState) float64 {
  571. cost := math.MaxFloat64
  572. for _, reader := range state.readerNodeIDs {
  573. tarNodes := state.nodesSortedByReader[reader]
  574. gotBlocks := bitmap.Bitmap64(0)
  575. thisCost := 0.0
  576. for _, tar := range tarNodes {
  577. tarNodeMp := state.nodeBlockBitmaps[tar.NodeID]
  578. // 只需要从目的节点上获得缺少的块
  579. curWeigth := gotBlocks.Weight()
  580. // 下面的if会在拿到k个块之后跳出循环,所以or多了块也没关系
  581. gotBlocks.Or(tarNodeMp)
  582. // 但是算读取块的消耗时,不能多算,最多算读了k个块的消耗
  583. willGetBlocks := math2.Min(gotBlocks.Weight()-curWeigth, state.object.minBlockCnt-curWeigth)
  584. thisCost += float64(willGetBlocks) * float64(tar.Distance)
  585. if gotBlocks.Weight() >= state.object.minBlockCnt {
  586. break
  587. }
  588. }
  589. if gotBlocks.Weight() >= state.object.minBlockCnt {
  590. cost = math.Min(cost, thisCost)
  591. }
  592. }
  593. return cost
  594. }
  595. // 计算冗余度
  596. func (t *CleanPinned) calcSpaceCost(ctx *annealingState) float64 {
  597. blockCount := 0
  598. for i, b := range ctx.blockList {
  599. if ctx.rmBlocks[i] {
  600. continue
  601. }
  602. if b.HasEntity {
  603. blockCount++
  604. }
  605. if b.HasShadow {
  606. blockCount++
  607. }
  608. }
  609. // 所有算力中心上拥有的块的总数 / 一个对象被分成了几个块
  610. return float64(blockCount) / float64(ctx.object.minBlockCnt)
  611. }
  612. // 如果新方案得分比旧方案小,那么在一定概率内也接受新方案
  613. func (t *CleanPinned) alwaysAccept(curTemp float64, dScore float64, coolingRate float64) bool {
  614. v := math.Exp(dScore / curTemp / coolingRate)
  615. // fmt.Printf(" -- chance: %v, temp: %v", v, curTemp)
  616. return v > rand.Float64()
  617. }
  618. func (t *CleanPinned) makePlansForRepObject(solu annealingSolution, obj stgmod.ObjectDetail, pinPlans map[cdssdk.NodeID]*[]string) coormq.UpdatingObjectRedundancy {
  619. entry := coormq.UpdatingObjectRedundancy{
  620. ObjectID: obj.Object.ObjectID,
  621. Redundancy: obj.Object.Redundancy,
  622. }
  623. for i, f := range solu.rmBlocks {
  624. hasCache := lo.ContainsBy(obj.Blocks, func(b stgmod.ObjectBlock) bool { return b.NodeID == solu.blockList[i].NodeID }) ||
  625. lo.ContainsBy(obj.PinnedAt, func(n cdssdk.NodeID) bool { return n == solu.blockList[i].NodeID })
  626. willRm := f
  627. if !willRm {
  628. // 如果对象在退火后要保留副本的节点没有副本,则需要在这个节点创建副本
  629. if !hasCache {
  630. pinPlan, ok := pinPlans[solu.blockList[i].NodeID]
  631. if !ok {
  632. pinPlan = &[]string{}
  633. pinPlans[solu.blockList[i].NodeID] = pinPlan
  634. }
  635. *pinPlan = append(*pinPlan, obj.Object.FileHash)
  636. }
  637. entry.Blocks = append(entry.Blocks, stgmod.ObjectBlock{
  638. ObjectID: obj.Object.ObjectID,
  639. Index: solu.blockList[i].Index,
  640. NodeID: solu.blockList[i].NodeID,
  641. FileHash: obj.Object.FileHash,
  642. })
  643. }
  644. }
  645. return entry
  646. }
  647. func (t *CleanPinned) makePlansForECObject(allNodeInfos map[cdssdk.NodeID]*cdssdk.Node, solu annealingSolution, obj stgmod.ObjectDetail, planBld *exec.PlanBuilder, planningNodeIDs map[cdssdk.NodeID]bool) coormq.UpdatingObjectRedundancy {
  648. entry := coormq.UpdatingObjectRedundancy{
  649. ObjectID: obj.Object.ObjectID,
  650. Redundancy: obj.Object.Redundancy,
  651. }
  652. reconstrct := make(map[cdssdk.NodeID]*[]int)
  653. for i, f := range solu.rmBlocks {
  654. block := solu.blockList[i]
  655. if !f {
  656. entry.Blocks = append(entry.Blocks, stgmod.ObjectBlock{
  657. ObjectID: obj.Object.ObjectID,
  658. Index: block.Index,
  659. NodeID: block.NodeID,
  660. FileHash: block.FileHash,
  661. })
  662. // 如果这个块是影子块,那么就要从完整对象里重建这个块
  663. if !block.HasEntity {
  664. re, ok := reconstrct[block.NodeID]
  665. if !ok {
  666. re = &[]int{}
  667. reconstrct[block.NodeID] = re
  668. }
  669. *re = append(*re, block.Index)
  670. }
  671. }
  672. }
  673. ecRed := obj.Object.Redundancy.(*cdssdk.ECRedundancy)
  674. parser := parser.NewParser(*ecRed)
  675. for id, idxs := range reconstrct {
  676. ft := ioswitch2.NewFromTo()
  677. ft.AddFrom(ioswitch2.NewFromNode(obj.Object.FileHash, allNodeInfos[id], -1))
  678. for _, i := range *idxs {
  679. ft.AddTo(ioswitch2.NewToNode(*allNodeInfos[id], i, fmt.Sprintf("%d.%d", obj.Object.ObjectID, i)))
  680. }
  681. err := parser.Parse(ft, planBld)
  682. if err != nil {
  683. // TODO 错误处理
  684. continue
  685. }
  686. planningNodeIDs[id] = true
  687. }
  688. return entry
  689. }
  690. func (t *CleanPinned) executePlans(execCtx ExecuteContext, pinPlans map[cdssdk.NodeID]*[]string, planBld *exec.PlanBuilder, plnningNodeIDs map[cdssdk.NodeID]bool) (map[string]any, error) {
  691. log := logger.WithType[CleanPinned]("Event")
  692. // 统一加锁,有重复也没关系
  693. lockBld := reqbuilder.NewBuilder()
  694. for nodeID := range pinPlans {
  695. lockBld.IPFS().Buzy(nodeID)
  696. }
  697. for id := range plnningNodeIDs {
  698. lockBld.IPFS().Buzy(id)
  699. }
  700. lock, err := lockBld.MutexLock(execCtx.Args.DistLock)
  701. if err != nil {
  702. return nil, fmt.Errorf("acquiring distlock: %w", err)
  703. }
  704. defer lock.Unlock()
  705. wg := sync.WaitGroup{}
  706. // 执行pin操作
  707. var anyPinErr error
  708. for nodeID, pin := range pinPlans {
  709. wg.Add(1)
  710. go func(nodeID cdssdk.NodeID, pin *[]string) {
  711. defer wg.Done()
  712. agtCli, err := stgglb.AgentMQPool.Acquire(nodeID)
  713. if err != nil {
  714. log.Warnf("new agent client: %s", err.Error())
  715. return
  716. }
  717. defer stgglb.AgentMQPool.Release(agtCli)
  718. _, err = agtCli.PinObject(agtmq.ReqPinObject(*pin, false))
  719. if err != nil {
  720. log.Warnf("pinning object: %s", err.Error())
  721. anyPinErr = err
  722. }
  723. }(nodeID, pin)
  724. }
  725. // 执行IO计划
  726. var ioSwRets map[string]any
  727. var ioSwErr error
  728. wg.Add(1)
  729. go func() {
  730. defer wg.Done()
  731. ret, err := planBld.Execute().Wait(context.TODO())
  732. if err != nil {
  733. ioSwErr = fmt.Errorf("executing io switch plan: %w", err)
  734. return
  735. }
  736. ioSwRets = ret
  737. }()
  738. wg.Wait()
  739. if anyPinErr != nil {
  740. return nil, anyPinErr
  741. }
  742. if ioSwErr != nil {
  743. return nil, ioSwErr
  744. }
  745. return ioSwRets, nil
  746. }
  747. func (t *CleanPinned) populateECObjectEntry(entry *coormq.UpdatingObjectRedundancy, obj stgmod.ObjectDetail, ioRets map[string]any) {
  748. for i := range entry.Blocks {
  749. if entry.Blocks[i].FileHash != "" {
  750. continue
  751. }
  752. key := fmt.Sprintf("%d.%d", obj.Object.ObjectID, entry.Blocks[i].Index)
  753. // 不应该出现key不存在的情况
  754. entry.Blocks[i].FileHash = ioRets[key].(string)
  755. }
  756. }
  757. func init() {
  758. RegisterMessageConvertor(NewCleanPinned)
  759. }

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