pod_container_dependencies.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package utils
  2. import (
  3. "yunion.io/x/pkg/errors"
  4. )
  5. type GetObjIdName[T any] func(T) string
  6. type GetDependencies[T any] func(T) []string
  7. func TopologicalSortContainers[T any](objs []T, getName GetObjIdName[T], getDependencies GetDependencies[T]) error {
  8. if len(objs) == 0 {
  9. return nil
  10. }
  11. // Build a dependency graph and an in-degree table
  12. graph := make(map[string][]string)
  13. inDegree := make(map[string]int)
  14. // init graph and inDegree
  15. for _, obj := range objs {
  16. inDegree[getName(obj)] = 0
  17. }
  18. for _, obj := range objs {
  19. oName := getName(obj)
  20. for _, dep := range getDependencies(obj) {
  21. if _, exists := inDegree[dep]; !exists {
  22. return errors.Errorf("The dependent container %s does not exist.", dep)
  23. }
  24. graph[dep] = append(graph[dep], oName)
  25. inDegree[oName]++
  26. }
  27. }
  28. // Topological sorting: use a queue to process nodes with an in-degree of 0
  29. queue := []string{}
  30. for name, degree := range inDegree {
  31. if degree == 0 {
  32. queue = append(queue, name)
  33. }
  34. }
  35. sorted := []string{}
  36. for len(queue) > 0 {
  37. current := queue[0]
  38. queue = queue[1:]
  39. sorted = append(sorted, current)
  40. // Decrease the in-degree of neighboring nodes
  41. for _, neighbor := range graph[current] {
  42. inDegree[neighbor]--
  43. if inDegree[neighbor] == 0 {
  44. queue = append(queue, neighbor)
  45. }
  46. }
  47. }
  48. // Check whether a cycle exists
  49. if len(sorted) != len(objs) {
  50. return errors.Errorf("There is a circular dependency among the dependencies.")
  51. }
  52. return nil
  53. }
  54. type DependencyTopoGraph[T any] struct {
  55. Graph map[string][]string `json:"graph,omitempty"`
  56. Degree map[string]int `json:"degree,omitempty"`
  57. Leafs []string `json:"leafs"` // containers whose in-degree is zero
  58. Finish *bool `json:"finish,omitempty"`
  59. }
  60. func NewDependencyTopoGraph[T any](
  61. objs []T,
  62. getId GetObjIdName[T],
  63. getName GetObjIdName[T],
  64. getDependencies GetDependencies[T],
  65. ) (*DependencyTopoGraph[T], error) {
  66. depGraph := &DependencyTopoGraph[T]{
  67. Graph: make(map[string][]string),
  68. Degree: make(map[string]int),
  69. Leafs: make([]string, 0, len(objs)),
  70. }
  71. nameToUUID := make(map[string]string)
  72. for _, obj := range objs {
  73. uuid := getId(obj)
  74. name := getName(obj)
  75. depGraph.Degree[uuid] = 0
  76. nameToUUID[name] = uuid
  77. }
  78. for _, obj := range objs {
  79. for _, dep := range getDependencies(obj) {
  80. depId := nameToUUID[dep]
  81. uuid := getId(obj)
  82. depGraph.Graph[depId] = append(depGraph.Graph[depId], uuid)
  83. depGraph.Degree[uuid]++
  84. }
  85. }
  86. for uuid, indegree := range depGraph.Degree {
  87. if indegree == 0 {
  88. depGraph.Leafs = append(depGraph.Leafs, uuid)
  89. }
  90. }
  91. return depGraph, nil
  92. }
  93. type FetchObjById[T any] func(string) T
  94. func (dep *DependencyTopoGraph[T]) GetNextBatch(fetchById FetchObjById[T]) []T {
  95. if len(dep.Leafs) == 0 {
  96. return nil
  97. }
  98. objs := make([]T, 0, len(dep.Leafs))
  99. nextLeafs := make([]string, 0)
  100. for _, uuid := range dep.Leafs {
  101. objs = append(objs, fetchById(uuid))
  102. for _, neighbor := range dep.Graph[uuid] {
  103. dep.Degree[neighbor]--
  104. if dep.Degree[neighbor] == 0 {
  105. nextLeafs = append(nextLeafs, neighbor)
  106. }
  107. }
  108. }
  109. // log.Infof("Get next batch:\n Leafs: %s\n nextLeafs: %s", dep.Leafs, nextLeafs)
  110. dep.Leafs = nextLeafs
  111. if len(nextLeafs) == 0 {
  112. finish := true
  113. dep.Finish = &finish
  114. }
  115. return objs
  116. }