github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/metrics/sinks/metric/metric_sink.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package metricsink
    16  
    17  import (
    18  	"sync"
    19  	"time"
    20  
    21  	"k8s.io/heapster/metrics/core"
    22  )
    23  
    24  // A simple in-memory storage for metrics. It divides metrics into 2 categories
    25  // * metrics that need to be stored for couple minutes.
    26  // * metrics that need to be stored for longer time (15 min, 1 hour).
    27  // The user of this struct needs to decide what are the long-stored metrics uprfront.
    28  type MetricSink struct {
    29  	// Request can come from other threads.
    30  	lock sync.Mutex
    31  
    32  	// List of metrics that will be stored for up to X seconds.
    33  	longStoreMetrics   []string
    34  	longStoreDuration  time.Duration
    35  	shortStoreDuration time.Duration
    36  
    37  	// Stores full DataBatch with all metrics and labels.
    38  	shortStore []*core.DataBatch
    39  	// Memory-efficient long/mid term storage for metrics.
    40  	longStore []*multimetricStore
    41  }
    42  
    43  // Stores values of a single metrics for different MetricSets.
    44  // Assumes that the user knows what the metric is.
    45  type int64Store map[string]int64
    46  
    47  type multimetricStore struct {
    48  	// Timestamp of the batch from which the metrics were taken.
    49  	timestamp time.Time
    50  	// Metric name to int64store with metric values.
    51  	store map[string]int64Store
    52  }
    53  
    54  type TimestampedMetricValue struct {
    55  	core.MetricValue
    56  	Timestamp time.Time
    57  }
    58  
    59  func buildMultimetricStore(metrics []string, batch *core.DataBatch) *multimetricStore {
    60  	store := multimetricStore{
    61  		timestamp: batch.Timestamp,
    62  		store:     make(map[string]int64Store, len(metrics)),
    63  	}
    64  	for _, metric := range metrics {
    65  		store.store[metric] = make(int64Store, len(batch.MetricSets))
    66  	}
    67  	for key, ms := range batch.MetricSets {
    68  		for _, metric := range metrics {
    69  			if metricValue, found := ms.MetricValues[metric]; found {
    70  				metricstore := store.store[metric]
    71  				metricstore[key] = metricValue.IntValue
    72  			}
    73  		}
    74  	}
    75  	return &store
    76  }
    77  
    78  func (this *MetricSink) Name() string {
    79  	return "Metric Sink"
    80  }
    81  
    82  func (this *MetricSink) Stop() {
    83  	// Do nothing.
    84  }
    85  
    86  func (this *MetricSink) ExportData(batch *core.DataBatch) {
    87  	this.lock.Lock()
    88  	defer this.lock.Unlock()
    89  
    90  	now := time.Now()
    91  	// TODO: add sorting
    92  	this.longStore = append(popOldStore(this.longStore, now.Add(-this.longStoreDuration)),
    93  		buildMultimetricStore(this.longStoreMetrics, batch))
    94  	this.shortStore = append(popOld(this.shortStore, now.Add(-this.shortStoreDuration)), batch)
    95  }
    96  
    97  func (this *MetricSink) GetLatestDataBatch() *core.DataBatch {
    98  	this.lock.Lock()
    99  	defer this.lock.Unlock()
   100  
   101  	if len(this.shortStore) == 0 {
   102  		return nil
   103  	}
   104  	return this.shortStore[len(this.shortStore)-1]
   105  }
   106  
   107  func (this *MetricSink) GetShortStore() []*core.DataBatch {
   108  	this.lock.Lock()
   109  	defer this.lock.Unlock()
   110  
   111  	result := make([]*core.DataBatch, 0, len(this.shortStore))
   112  	for _, batch := range this.shortStore {
   113  		result = append(result, batch)
   114  	}
   115  	return result
   116  }
   117  
   118  func (this *MetricSink) GetMetric(metricName string, keys []string, start, end time.Time) map[string][]TimestampedMetricValue {
   119  	this.lock.Lock()
   120  	defer this.lock.Unlock()
   121  
   122  	useLongStore := false
   123  	for _, longStoreMetric := range this.longStoreMetrics {
   124  		if longStoreMetric == metricName {
   125  			useLongStore = true
   126  		}
   127  	}
   128  
   129  	result := make(map[string][]TimestampedMetricValue)
   130  	if useLongStore {
   131  		for _, store := range this.longStore {
   132  			// Inclusive start and end.
   133  			if !store.timestamp.Before(start) && !store.timestamp.After(end) {
   134  				substore := store.store[metricName]
   135  				for _, key := range keys {
   136  					if val, found := substore[key]; found {
   137  						result[key] = append(result[key], TimestampedMetricValue{
   138  							Timestamp: store.timestamp,
   139  							MetricValue: core.MetricValue{
   140  								IntValue:   val,
   141  								ValueType:  core.ValueInt64,
   142  								MetricType: core.MetricGauge,
   143  							},
   144  						})
   145  					}
   146  				}
   147  			}
   148  		}
   149  	} else {
   150  		for _, batch := range this.shortStore {
   151  			// Inclusive start and end.
   152  			if !batch.Timestamp.Before(start) && !batch.Timestamp.After(end) {
   153  				for _, key := range keys {
   154  					metricSet, found := batch.MetricSets[key]
   155  					if !found {
   156  						continue
   157  					}
   158  					metricValue, found := metricSet.MetricValues[metricName]
   159  					if !found {
   160  						continue
   161  					}
   162  					keyResult, found := result[key]
   163  					if !found {
   164  						keyResult = make([]TimestampedMetricValue, 0)
   165  					}
   166  					keyResult = append(keyResult, TimestampedMetricValue{
   167  						Timestamp:   batch.Timestamp,
   168  						MetricValue: metricValue,
   169  					})
   170  					result[key] = keyResult
   171  				}
   172  			}
   173  		}
   174  	}
   175  	return result
   176  }
   177  
   178  func (this *MetricSink) GetMetricNames(key string) []string {
   179  	this.lock.Lock()
   180  	defer this.lock.Unlock()
   181  
   182  	metricNames := make(map[string]bool)
   183  	for _, batch := range this.shortStore {
   184  		if set, found := batch.MetricSets[key]; found {
   185  			for key := range set.MetricValues {
   186  				metricNames[key] = true
   187  			}
   188  		}
   189  	}
   190  	result := make([]string, 0, len(metricNames))
   191  	for key := range metricNames {
   192  		result = append(result, key)
   193  	}
   194  	return result
   195  }
   196  
   197  func (this *MetricSink) getAllNames(predicate func(ms *core.MetricSet) bool,
   198  	name func(key string, ms *core.MetricSet) string) []string {
   199  	this.lock.Lock()
   200  	defer this.lock.Unlock()
   201  
   202  	if len(this.shortStore) == 0 {
   203  		return []string{}
   204  	}
   205  
   206  	result := make([]string, 0, 0)
   207  	for key, value := range this.shortStore[len(this.shortStore)-1].MetricSets {
   208  		if predicate(value) {
   209  			result = append(result, name(key, value))
   210  		}
   211  	}
   212  	return result
   213  }
   214  
   215  /*
   216   * For debugging only.
   217   */
   218  func (this *MetricSink) GetMetricSetKeys() []string {
   219  	return this.getAllNames(
   220  		func(ms *core.MetricSet) bool { return true },
   221  		func(key string, ms *core.MetricSet) string { return key })
   222  }
   223  
   224  func (this *MetricSink) GetNodes() []string {
   225  	return this.getAllNames(
   226  		func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNode },
   227  		func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelHostname.Key] })
   228  }
   229  
   230  func (this *MetricSink) GetPods() []string {
   231  	return this.getAllNames(
   232  		func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod },
   233  		func(key string, ms *core.MetricSet) string {
   234  			return ms.Labels[core.LabelNamespaceName.Key] + "/" + ms.Labels[core.LabelPodName.Key]
   235  		})
   236  }
   237  
   238  func (this *MetricSink) GetNamespaces() []string {
   239  	return this.getAllNames(
   240  		func(ms *core.MetricSet) bool {
   241  			return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNamespace
   242  		},
   243  		func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelNamespaceName.Key] })
   244  }
   245  
   246  func (this *MetricSink) GetPodsFromNamespace(namespace string) []string {
   247  	return this.getAllNames(
   248  		func(ms *core.MetricSet) bool {
   249  			return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod &&
   250  				ms.Labels[core.LabelNamespaceName.Key] == namespace
   251  		},
   252  		func(key string, ms *core.MetricSet) string {
   253  			return ms.Labels[core.LabelPodName.Key]
   254  		})
   255  }
   256  
   257  func (this *MetricSink) GetContainersForPodFromNamespace(namespace, pod string) []string {
   258  	return this.getAllNames(
   259  		func(ms *core.MetricSet) bool {
   260  			return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePodContainer &&
   261  				ms.Labels[core.LabelNamespaceName.Key] == namespace &&
   262  				ms.Labels[core.LabelPodName.Key] == pod
   263  		},
   264  		func(key string, ms *core.MetricSet) string {
   265  			return ms.Labels[core.LabelContainerName.Key]
   266  		})
   267  }
   268  
   269  func (this *MetricSink) GetSystemContainersFromNode(node string) []string {
   270  	return this.getAllNames(
   271  		func(ms *core.MetricSet) bool {
   272  			return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeSystemContainer &&
   273  				ms.Labels[core.LabelHostname.Key] == node
   274  		},
   275  		func(key string, ms *core.MetricSet) string {
   276  			return ms.Labels[core.LabelContainerName.Key]
   277  		})
   278  }
   279  
   280  func popOld(storage []*core.DataBatch, cutoffTime time.Time) []*core.DataBatch {
   281  	result := make([]*core.DataBatch, 0)
   282  	for _, batch := range storage {
   283  		if batch.Timestamp.After(cutoffTime) {
   284  			result = append(result, batch)
   285  		}
   286  	}
   287  	return result
   288  }
   289  
   290  func popOldStore(storages []*multimetricStore, cutoffTime time.Time) []*multimetricStore {
   291  	result := make([]*multimetricStore, 0, len(storages))
   292  	for _, store := range storages {
   293  		if store.timestamp.After(cutoffTime) {
   294  			result = append(result, store)
   295  		}
   296  	}
   297  	return result
   298  }
   299  
   300  func NewMetricSink(shortStoreDuration, longStoreDuration time.Duration, longStoreMetrics []string) *MetricSink {
   301  	return &MetricSink{
   302  		longStoreMetrics:   longStoreMetrics,
   303  		longStoreDuration:  longStoreDuration,
   304  		shortStoreDuration: shortStoreDuration,
   305  		longStore:          make([]*multimetricStore, 0),
   306  		shortStore:         make([]*core.DataBatch, 0),
   307  	}
   308  }