2
0

index.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package scrape
  2. import (
  3. "sync"
  4. "time"
  5. "github.com/opencost/opencost/core/pkg/clustercache"
  6. "github.com/opencost/opencost/core/pkg/log"
  7. "k8s.io/apimachinery/pkg/types"
  8. )
  9. // persistedIndexTTL is how long an index entry is retained after it was last seen in the cluster cache. It needs to
  10. // outlast the window where dependent objects (e.g. pods on a deleted node) remain in the cache after their referent
  11. // has been removed.
  12. const persistedIndexTTL = time.Hour
  13. type persistedIndexEntry struct {
  14. uid types.UID
  15. lastSeen time.Time
  16. }
  17. // persistedIndex retains key to UID mappings across scrapes. Objects are not always removed from the cluster cache in
  18. // dependency order, so an object can outlive the object it references by name (e.g. a pod whose node has already been
  19. // deleted). Retaining recently seen entries prevents these lookups from resolving to an empty UID, which would
  20. // overwrite the previously scraped value. A nil *persistedIndex performs no retention.
  21. type persistedIndex[K comparable] struct {
  22. name string
  23. lock sync.Mutex
  24. entries map[K]persistedIndexEntry
  25. }
  26. func newPersistedIndex[K comparable](name string) *persistedIndex[K] {
  27. return &persistedIndex[K]{
  28. name: name,
  29. entries: make(map[K]persistedIndexEntry),
  30. }
  31. }
  32. // update records the entries of current, which always take precedence over retained entries, evicts entries which
  33. // have not been seen within the ttl, and returns a new index containing current along with any retained entries.
  34. func (pi *persistedIndex[K]) update(current map[K]types.UID) map[K]types.UID {
  35. if pi == nil {
  36. return current
  37. }
  38. pi.lock.Lock()
  39. defer pi.lock.Unlock()
  40. now := time.Now()
  41. for key, uid := range current {
  42. pi.entries[key] = persistedIndexEntry{uid: uid, lastSeen: now}
  43. }
  44. result := make(map[K]types.UID, len(pi.entries))
  45. for key, entry := range pi.entries {
  46. if now.Sub(entry.lastSeen) > persistedIndexTTL {
  47. delete(pi.entries, key)
  48. continue
  49. }
  50. if _, ok := current[key]; !ok {
  51. log.Debugf("%s index: retaining UID '%s' for '%v' which is no longer in the cluster cache", pi.name, entry.uid, key)
  52. }
  53. result[key] = entry.uid
  54. }
  55. return result
  56. }
  57. // pvcKey is a composite key for a PersistentVolumeClaim (name + namespace).
  58. type pvcKey struct {
  59. name string
  60. namespace string
  61. }
  62. // buildNodeIndex returns a map from node name to UID.
  63. func buildNodeIndex(nodes []*clustercache.Node) map[string]types.UID {
  64. m := make(map[string]types.UID, len(nodes))
  65. for _, node := range nodes {
  66. m[node.Name] = node.UID
  67. }
  68. return m
  69. }
  70. // buildNamespaceIndex returns a map from namespace name to UID.
  71. func buildNamespaceIndex(namespaces []*clustercache.Namespace) map[string]types.UID {
  72. m := make(map[string]types.UID, len(namespaces))
  73. for _, ns := range namespaces {
  74. m[ns.Name] = ns.UID
  75. }
  76. return m
  77. }
  78. // buildPVCIndex returns a map from (name, namespace) to PVC UID.
  79. func buildPVCIndex(pvcs []*clustercache.PersistentVolumeClaim) map[pvcKey]types.UID {
  80. m := make(map[pvcKey]types.UID, len(pvcs))
  81. for _, pvc := range pvcs {
  82. m[pvcKey{name: pvc.Name, namespace: pvc.Namespace}] = pvc.UID
  83. }
  84. return m
  85. }
  86. // buildPVIndex returns a map from PV name to UID.
  87. func buildPVIndex(pvs []*clustercache.PersistentVolume) map[string]types.UID {
  88. m := make(map[string]types.UID, len(pvs))
  89. for _, pv := range pvs {
  90. m[pv.Name] = pv.UID
  91. }
  92. return m
  93. }
  94. // podKey is a composite key for a Pod (namespace + name).
  95. type podKey struct {
  96. namespace string
  97. name string
  98. }
  99. // buildPodIndex returns a map from (namespace, name) to Pod UID.
  100. func buildPodIndex(pods []*clustercache.Pod) map[podKey]types.UID {
  101. m := make(map[podKey]types.UID, len(pods))
  102. for _, pod := range pods {
  103. m[podKey{namespace: pod.Namespace, name: pod.Name}] = pod.UID
  104. }
  105. return m
  106. }