Skip to content

Commit

Permalink
Merge pull request #1 from stone-z/curry-tests
Browse files Browse the repository at this point in the history
Drop LabelValues partial match, add tests
  • Loading branch information
stone-z committed Mar 28, 2022
2 parents 9a8cb32 + a3a1a58 commit 0aae467
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 105 deletions.
82 changes: 21 additions & 61 deletions prometheus/vec.go
Expand Up @@ -80,16 +80,6 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
}

// DeletePartialMatchLabelValues deletes all metrics where the variable labels
// contain all of the values passed. The order of the passed values does not matter.
// It returns the number of metrics deleted.

// This method deletes a metric if the given value is present for any label.
// To delete a metric based on a partial match for a particular label, use DeletePartialMatch().
func (m *MetricVec) DeletePartialMatchLabelValues(lvs ...string) int {
return m.metricMap.deleteByLabelValues(lvs)
}

// Delete deletes the metric where the variable labels are the same as those
// passed in as labels. It returns true if a metric was deleted.
//
Expand All @@ -116,7 +106,7 @@ func (m *MetricVec) Delete(labels Labels) bool {
// This method deletes a metric if the given label: value pair is found in that metric.
// To delete a metric if a particular value is found associated with any label, use DeletePartialMatchLabelValues().
func (m *MetricVec) DeletePartialMatch(labels Labels) int {
return m.metricMap.deleteByLabels(labels)
return m.metricMap.deleteByLabels(labels, m.curry)
}

// Without explicit forwarding of Describe, Collect, Reset, those methods won't
Expand Down Expand Up @@ -373,51 +363,6 @@ func (m *metricMap) deleteByHashWithLabelValues(
return true
}

// deleteByLabelValues deletes a metric if the given values (lvs) are present in the metric.
func (m *metricMap) deleteByLabelValues(lvs []string) int {
m.mtx.Lock()
defer m.mtx.Unlock()

var numDeleted int

for h, metrics := range m.metrics {
i := findMetricWithPartialLabelValues(metrics, lvs)
if i >= len(metrics) {
// Didn't find matching label values in this metric slice.
continue
}
delete(m.metrics, h)
numDeleted++
}

return numDeleted
}

// findMetricWithPartialLabelValues returns the index of the matching metric or
// len(metrics) if not found.
func findMetricWithPartialLabelValues(
metrics []metricWithLabelValues, lvs []string,
) int {
for i, metric := range metrics {
if matchPartialLabelValues(metric.values, lvs) {
return i
}
}
return len(metrics)
}

// matchPartialLabelValues searches the current metric values and returns whether all of the target values are present.
func matchPartialLabelValues(values []string, lvs []string) bool {
for _, v := range lvs {
// Check if the target value exists in our metrics and get the index.
if _, validValue := indexOf(v, values); validValue {
continue
}
return false
}
return true
}

// deleteByHashWithLabels removes the metric from the hash bucket h. If there
// are multiple matches in the bucket, use lvs to select a metric and remove
// only that metric.
Expand Down Expand Up @@ -447,14 +392,14 @@ func (m *metricMap) deleteByHashWithLabels(
}

// deleteByLabels deletes a metric if the given labels are present in the metric.
func (m *metricMap) deleteByLabels(labels Labels) int {
func (m *metricMap) deleteByLabels(labels Labels, curry []curriedLabelValue) int {
m.mtx.Lock()
defer m.mtx.Unlock()

var numDeleted int

for h, metrics := range m.metrics {
i := findMetricWithPartialLabels(m.desc, metrics, labels)
i := findMetricWithPartialLabels(m.desc, metrics, labels, curry)
if i >= len(metrics) {
// Didn't find matching labels in this metric slice.
continue
Expand All @@ -469,10 +414,10 @@ func (m *metricMap) deleteByLabels(labels Labels) int {
// findMetricWithPartialLabel returns the index of the matching metric or
// len(metrics) if not found.
func findMetricWithPartialLabels(
desc *Desc, metrics []metricWithLabelValues, labels Labels,
desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
) int {
for i, metric := range metrics {
if matchPartialLabels(desc, metric.values, labels) {
if matchPartialLabels(desc, metric.values, labels, curry) {
return i
}
}
Expand All @@ -490,13 +435,28 @@ func indexOf(target string, items []string) (int, bool) {
return len(items), false
}

// valueOrCurriedValue determines if a value was previously curried,
// and returns either the "base" value or the curried value accordingly.
func valueOrCurriedValue(index int, values []string, curry []curriedLabelValue) string {
for _, curriedValue := range curry {
if curriedValue.index == index {
return curriedValue.value
}
}
return values[index]
}

// matchPartialLabels searches the current metric and returns whether all of the target label:value pairs are present.
func matchPartialLabels(desc *Desc, values []string, labels Labels) bool {
func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
for l, v := range labels {
// Check if the target label exists in our metrics and get the index.
varLabelIndex, validLabel := indexOf(l, desc.variableLabels)
if validLabel {
// Check the value of that label against the target value.
// if valueOrCurriedValue(varLabelIndex, values, curry) == v {
// continue
// }

if values[varLabelIndex] == v {
continue

Expand Down
60 changes: 16 additions & 44 deletions prometheus/vec_test.go
Expand Up @@ -164,71 +164,43 @@ func testDeletePartialMatch(t *testing.T, vec *GaugeVec) {
// Try to delete with a single valid label which matches multiple metrics.
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
vec.With(Labels{"l1": "v1", "l2": "vv22"}).(Gauge).Set(84)
c3 := vec.MustCurryWith(Labels{"l2": "l2C3CurriedValue"}) // Used below
vec.With(Labels{"l1": "v3", "l2": "v3"}).(Gauge).Set(168)
if got, want := vec.DeletePartialMatch(Labels{"l1": "v1"}), 2; got != want {
t.Errorf("got %v, want %v", got, want)
}

// Try to delete a value which shouldn't be in our base vector (only the curried one c3).
if got, want := vec.DeletePartialMatch(Labels{"l2": "l2C3CurriedValue"}), 0; got != want {
t.Errorf("got %v, want %v", got, want)
}

c2 := vec.MustCurryWith(Labels{"l2": "l2CurriedValue"})
c2.With(Labels{"l1": "11"}).Inc()

// Delete with valid curried pair l2: l2CurriedValue.
if got, want := c2.DeletePartialMatch(Labels{"l2": "l2CurriedValue"}), 1; got != want {
t.Errorf("got %v, want %v", got, want)
}

// Same labels, value matches.
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
if got, want := vec.DeletePartialMatch(Labels{"l1": "v1"}), 1; got != want {
// Try to delete with a label value from before currying.
if got, want := c2.DeletePartialMatch(Labels{"l2": "v3"}), 0; got != want {
t.Errorf("got %v, want %v", got, want)
}
}

func TestDeletePartialMatchLabelValues(t *testing.T) {
vec := NewGaugeVec(
GaugeOpts{
Name: "test",
Help: "helpless",
},
[]string{"l1", "l2"},
)
testDeletePartialMatchLabelValues(t, vec)
}

func testDeletePartialMatchLabelValues(t *testing.T, vec *GaugeVec) {
// No metric value is set.
if got, want := vec.DeletePartialMatchLabelValues("v1", "v2"), 0; got != want {
t.Errorf("got %v, want %v", got, want)
}
c3.With(Labels{"l1": "11"}).Inc()

// Try to delete with a single valid value.
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
if got, want := vec.DeletePartialMatchLabelValues("v1"), 1; got != want {
// Try to delete with invalid curried pair l1: v1.
if got, want := c3.DeletePartialMatch(Labels{"l1": "v1"}), 0; got != want {
t.Errorf("got %v, want %v", got, want)
}

// Try to delete with partially invalid values.
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
if got, want := vec.DeletePartialMatchLabelValues("v1", "xv2"), 0; got != want {
// Delete valid curried pair l2: l2C3CurriedValue.
if got, want := c3.DeletePartialMatch(Labels{"l2": "l2C3CurriedValue"}), 1; got != want {
t.Errorf("got %v, want %v", got, want)
}

// Try to delete with a single valid value which matches multiple metrics.
// Same labels, value matches.
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Gauge).Set(42)
vec.With(Labels{"l1": "v1", "l2": "vv22"}).(Gauge).Set(84)
if got, want := vec.DeletePartialMatchLabelValues("v1"), 2; got != want {
t.Errorf("got %v, want %v", got, want)
}

c1 := vec.MustCurryWith(Labels{"l1": "cv1"})
c1.WithLabelValues("2").Inc()

// Try to delete with nonexistent value z1.
if got, want := c1.DeletePartialMatchLabelValues("z1"), 0; got != want {
t.Errorf("got %v, want %v", got, want)
}

// Delete with valid curried value cv1.
if got, want := c1.DeletePartialMatchLabelValues("cv1"), 1; got != want {
if got, want := vec.DeletePartialMatch(Labels{"l1": "v1"}), 1; got != want {
t.Errorf("got %v, want %v", got, want)
}
}
Expand Down

0 comments on commit 0aae467

Please sign in to comment.