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  }