github.com/kubewharf/katalyst-core@v0.5.3/pkg/custom-metric/store/data/cache.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package data 18 19 import ( 20 "fmt" 21 "strings" 22 "sync" 23 "time" 24 25 "k8s.io/apimachinery/pkg/labels" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 28 "github.com/kubewharf/katalyst-core/pkg/custom-metric/store/data/internal" 29 "github.com/kubewharf/katalyst-core/pkg/custom-metric/store/data/types" 30 "github.com/kubewharf/katalyst-core/pkg/metrics" 31 "github.com/kubewharf/katalyst-core/pkg/util/general" 32 ) 33 34 const ( 35 metricsNameKCMASStoreDataGetCost = "kcmas_store_data_cost_get" 36 metricsNameKCMASStoreDataLength = "kcmas_store_data_length" 37 metricNameKCMASStoreQueryNotHitIndex = "kcmas_store_query_not_hit_index" 38 39 bucketSize = 256 40 ) 41 42 // CachedMetric stores all metricItems in an organized way. 43 // the aggregation logic will be performed in this package. 44 type CachedMetric struct { 45 sync.RWMutex 46 emitter metrics.MetricEmitter 47 metricMap map[types.MetricMeta]ObjectMetricStore 48 storeType ObjectMetricStoreType 49 } 50 51 func NewCachedMetric(metricsEmitter metrics.MetricEmitter, storeType ObjectMetricStoreType) *CachedMetric { 52 return &CachedMetric{ 53 emitter: metricsEmitter, 54 metricMap: make(map[types.MetricMeta]ObjectMetricStore), 55 storeType: storeType, 56 } 57 } 58 59 func (c *CachedMetric) addNewObjectMetricStore(metricMeta types.MetricMetaImp) (ObjectMetricStore, error) { 60 c.Lock() 61 defer c.Unlock() 62 63 if _, ok := c.metricMap[metricMeta]; !ok { 64 switch c.storeType { 65 case ObjectMetricStoreTypeBucket: 66 c.metricMap[metricMeta] = NewBucketObjectMetricStore(bucketSize, metricMeta) 67 case ObjectMetricStoreTypeSimple: 68 c.metricMap[metricMeta] = NewSimpleObjectMetricStore(metricMeta) 69 default: 70 return nil, fmt.Errorf("unsupported store type: %v", c.storeType) 71 } 72 } 73 74 return c.metricMap[metricMeta], nil 75 } 76 77 func (c *CachedMetric) getObjectMetricStore(metricMeta types.MetricMetaImp) ObjectMetricStore { 78 c.RLock() 79 defer c.RUnlock() 80 81 return c.metricMap[metricMeta] 82 } 83 84 func (c *CachedMetric) AddSeriesMetric(sList ...types.Metric) error { 85 start := time.Now() 86 87 var needReAggregate []*internal.MetricImp 88 for _, s := range sList { 89 d, ok := s.(*types.SeriesMetric) 90 if !ok || d == nil || len(d.GetItemList()) == 0 || d.GetName() == "" { 91 continue 92 } 93 94 objectMetricStore := c.getObjectMetricStore(d.MetricMetaImp) 95 if objectMetricStore == nil { 96 var err error 97 objectMetricStore, err = c.addNewObjectMetricStore(d.MetricMetaImp) 98 if err != nil { 99 return err 100 } 101 } 102 103 exists, err := objectMetricStore.ObjectExists(d.ObjectMetaImp) 104 if err != nil { 105 return err 106 } 107 if !exists { 108 err := objectMetricStore.Add(d.ObjectMetaImp) 109 if err != nil { 110 return err 111 } 112 } 113 internalMetric, getErr := objectMetricStore.GetInternalMetricImp(d.ObjectMetaImp) 114 if getErr != nil { 115 return getErr 116 } 117 118 added := internalMetric.AddSeriesMetric(d) 119 if len(added) > 0 { 120 needReAggregate = append(needReAggregate, internalMetric) 121 latestTimestamp := internalMetric.GetLatestTimestamp() 122 costs := start.Sub(time.UnixMilli(latestTimestamp)).Microseconds() 123 general.InfofV(6, "set cache,metric name: %v, series length: %v, add length:%v, latest timestamp: %v, costs: %v(microsecond)", d.MetricMetaImp.Name, 124 s.Len(), len(added), latestTimestamp, costs) 125 } 126 } 127 128 for _, i := range needReAggregate { 129 i.AggregateMetric() 130 } 131 return nil 132 } 133 134 // ListAllMetricMeta returns all metric meta with a flattened slice 135 func (c *CachedMetric) ListAllMetricMeta(withObject bool) []types.MetricMeta { 136 c.RLock() 137 defer c.RUnlock() 138 139 var res []types.MetricMeta 140 for metricMeta := range c.metricMap { 141 if (withObject && metricMeta.GetObjectKind() == "") || 142 (!withObject && metricMeta.GetObjectKind() != "") { 143 continue 144 } 145 res = append(res, metricMeta) 146 } 147 return res 148 } 149 150 // ListAllMetricNames returns all metric with a flattened slice, but only contain names 151 func (c *CachedMetric) ListAllMetricNames() []string { 152 c.RLock() 153 defer c.RUnlock() 154 155 var res []string 156 for metricMeta, objectMetricStore := range c.metricMap { 157 if objectMetricStore.Len() == 0 { 158 continue 159 } 160 res = append(res, metricMeta.GetName()) 161 } 162 return res 163 } 164 165 func (c *CachedMetric) GetMetric(namespace, metricName string, objName string, objectMetaList []types.ObjectMetaImp, usingObjectMetaList bool, gr *schema.GroupResource, metricSelector labels.Selector, latest bool) ([]types.Metric, bool, error) { 166 start := time.Now() 167 originMetricName, aggName := types.ParseAggregator(metricName) 168 169 defer func() { 170 _ = c.emitter.StoreInt64(metricsNameKCMASStoreDataGetCost, time.Now().Sub(start).Microseconds(), metrics.MetricTypeNameRaw) 171 }() 172 173 var res []types.Metric 174 metricMeta := types.MetricMetaImp{ 175 Name: originMetricName, 176 Namespaced: namespace != "", 177 } 178 if gr != nil { 179 metricMeta.ObjectKind = gr.String() 180 } 181 182 handleInternalMetric := func(internalMetric *internal.MetricImp) { 183 if internalMetric == nil { 184 return 185 } 186 187 if internalMetric.GetObjectNamespace() != namespace || (objName != "" && internalMetric.GetObjectName() != objName) { 188 return 189 } 190 191 if aggName == "" { 192 metricItems, exist := internalMetric.GetSeriesItems(metricSelector, latest) 193 if exist && len(metricItems) > 0 { 194 for i := range metricItems { 195 res = append(res, metricItems[i]) 196 } 197 } 198 } else { 199 metricItem, exist := internalMetric.GetAggregatedItems(metricSelector, aggName) 200 if exist && len(metricItem) > 0 { 201 res = append(res, metricItem...) 202 } 203 } 204 } 205 206 objectMetricStore := c.getObjectMetricStore(metricMeta) 207 if objectMetricStore != nil { 208 if usingObjectMetaList { 209 if len(objectMetaList) > 0 { 210 // get by object list selected by caller 211 for _, objectMeta := range objectMetaList { 212 internalMetric, err := objectMetricStore.GetInternalMetricImp(objectMeta) 213 if err != nil { 214 return nil, false, err 215 } 216 if internalMetric == nil { 217 continue 218 } 219 handleInternalMetric(internalMetric) 220 } 221 } 222 } else { 223 _ = c.emitter.StoreInt64(metricNameKCMASStoreQueryNotHitIndex, 1, metrics.MetricTypeNameRaw, 224 metrics.MetricTag{Key: "metric_name", Val: metricName}) 225 objectMetricStore.Iterate(func(internalMetric *internal.MetricImp) { 226 handleInternalMetric(internalMetric) 227 }) 228 } 229 230 return res, true, nil 231 } 232 233 return nil, false, nil 234 } 235 236 // GetAllMetricsInNamespace & GetAllMetricsInNamespaceWithLimit may be too time-consuming, 237 // so we should ensure that client falls into this functions as less frequent as possible. 238 func (c *CachedMetric) GetAllMetricsInNamespace(namespace string) []types.Metric { 239 c.RLock() 240 defer c.RUnlock() 241 242 var res []types.Metric 243 for _, internalMap := range c.metricMap { 244 internalMap.Iterate(func(internalMetric *internal.MetricImp) { 245 if internalMetric.GetObjectNamespace() != namespace { 246 return 247 } 248 249 metricItems, exist := internalMetric.GetSeriesItems(nil, false) 250 if exist && len(metricItems) > 0 { 251 for i := range metricItems { 252 if metricItems[i].Len() > 0 { 253 res = append(res, metricItems[i].DeepCopy()) 254 } 255 } 256 } 257 }) 258 } 259 260 return res 261 } 262 263 func (c *CachedMetric) GC(expiredTime time.Time) { 264 c.gcWithTimestamp(expiredTime.UnixMilli()) 265 } 266 267 func (c *CachedMetric) gcWithTimestamp(expiredTimestamp int64) { 268 c.RLock() 269 defer c.RUnlock() 270 271 dataLength := 0 272 273 for _, objectMetricStore := range c.metricMap { 274 objectMetricStore.Iterate(func(internalMetric *internal.MetricImp) { 275 internalMetric.GC(expiredTimestamp) 276 if internalMetric.Len() != 0 { 277 dataLength += internalMetric.Len() 278 } 279 }) 280 } 281 282 _ = c.emitter.StoreInt64(metricsNameKCMASStoreDataLength, int64(dataLength), metrics.MetricTypeNameRaw) 283 } 284 285 func (c *CachedMetric) Purge() { 286 c.RLock() 287 defer c.RUnlock() 288 289 for _, store := range c.metricMap { 290 store.Purge() 291 } 292 } 293 294 // MergeInternalMetricList merges internal metric lists and sort them 295 // for series: if the same timestamp appears in different list, randomly choose one item. 296 // for aggregated: we will just skip the duplicated items 297 func MergeInternalMetricList(metricName string, metricLists ...[]types.Metric) []types.Metric { 298 if len(metricLists) == 0 { 299 return []types.Metric{} 300 } else if len(metricLists) == 1 { 301 return metricLists[0] 302 } 303 304 var res []types.Metric 305 c := NewCachedMetric(metrics.DummyMetrics{}, ObjectMetricStoreTypeSimple) 306 307 _, aggName := types.ParseAggregator(metricName) 308 if len(aggName) == 0 { 309 for _, metricList := range metricLists { 310 _ = c.AddSeriesMetric(metricList...) 311 } 312 for _, objectMetricStore := range c.metricMap { 313 objectMetricStore.Iterate(func(internalMetric *internal.MetricImp) { 314 if metricItems, exist := internalMetric.GetSeriesItems(nil, false); exist && len(metricItems) > 0 { 315 for i := range metricItems { 316 res = append(res, metricItems[i]) 317 } 318 } 319 }) 320 } 321 } else { 322 merger := newAggregatedMetricMerger() 323 for _, metricList := range metricLists { 324 merger.addMetrics(metricList...) 325 } 326 res = merger.getMergedMetrics() 327 } 328 329 return res 330 } 331 332 // AggregatedMetricMerger is used to merge aggregated metric 333 type aggregatedMetricMerger struct { 334 metrics map[string]*types.AggregatedMetric 335 } 336 337 func newAggregatedMetricMerger() *aggregatedMetricMerger { 338 return &aggregatedMetricMerger{ 339 metrics: make(map[string]*types.AggregatedMetric), 340 } 341 } 342 343 func (a *aggregatedMetricMerger) getMetricKey(metric *types.AggregatedMetric) string { 344 return strings.Join([]string{metric.GetObjectNamespace(), metric.GetName(), metric.GetObjectKind(), metric.GetObjectName(), metric.BasicMetric.String()}, ":") 345 } 346 347 func (a *aggregatedMetricMerger) addMetrics(metrics ...types.Metric) { 348 for i := range metrics { 349 aggMetric := metrics[i].(*types.AggregatedMetric) 350 key := a.getMetricKey(aggMetric) 351 if oldMetric, ok := a.metrics[key]; ok { 352 if aggMetric.AggregatedIdentity.Timestamp > oldMetric.AggregatedIdentity.Timestamp { 353 a.metrics[key] = aggMetric 354 } 355 } else { 356 a.metrics[key] = aggMetric 357 } 358 } 359 } 360 361 func (a *aggregatedMetricMerger) getMergedMetrics() []types.Metric { 362 var res []types.Metric 363 for _, metric := range a.metrics { 364 res = append(res, metric) 365 } 366 return res 367 }