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

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

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