github.com/newrelic/go-agent@v3.26.0+incompatible/internal/metrics_test.go (about)

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package internal
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  var (
    14  	start = time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC)
    15  	end   = time.Date(2014, time.November, 28, 1, 2, 0, 0, time.UTC)
    16  )
    17  
    18  func TestEmptyMetrics(t *testing.T) {
    19  	mt := newMetricTable(20, start)
    20  	js, err := mt.CollectorJSON(`12345`, end)
    21  	if nil != err {
    22  		t.Fatal(err)
    23  	}
    24  	if nil != js {
    25  		t.Error(string(js))
    26  	}
    27  }
    28  
    29  func isValidJSON(data []byte) error {
    30  	var v interface{}
    31  
    32  	return json.Unmarshal(data, &v)
    33  }
    34  
    35  func TestMetrics(t *testing.T) {
    36  	mt := newMetricTable(20, start)
    37  
    38  	mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
    39  	mt.addDuration("two", "my_scope", 4*time.Second, 2*time.Second, unforced)
    40  	mt.addDuration("one", "my_scope", 2*time.Second, 1*time.Second, unforced)
    41  	mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
    42  
    43  	mt.addApdex("apdex satisfied", "", 9*time.Second, ApdexSatisfying, unforced)
    44  	mt.addApdex("apdex satisfied", "", 8*time.Second, ApdexSatisfying, unforced)
    45  	mt.addApdex("apdex tolerated", "", 7*time.Second, ApdexTolerating, unforced)
    46  	mt.addApdex("apdex tolerated", "", 8*time.Second, ApdexTolerating, unforced)
    47  	mt.addApdex("apdex failed", "my_scope", 1*time.Second, ApdexFailing, unforced)
    48  
    49  	mt.addCount("count 123", float64(123), unforced)
    50  	mt.addSingleCount("count 1", unforced)
    51  
    52  	ExpectMetrics(t, mt, []WantMetric{
    53  		{"apdex satisfied", "", false, []float64{2, 0, 0, 8, 9, 0}},
    54  		{"apdex tolerated", "", false, []float64{0, 2, 0, 7, 8, 0}},
    55  		{"one", "", false, []float64{2, 4, 2, 2, 2, 8}},
    56  		{"apdex failed", "my_scope", false, []float64{0, 0, 1, 1, 1, 0}},
    57  		{"one", "my_scope", false, []float64{1, 2, 1, 2, 2, 4}},
    58  		{"two", "my_scope", false, []float64{1, 4, 2, 4, 4, 16}},
    59  		{"count 123", "", false, []float64{123, 0, 0, 0, 0, 0}},
    60  		{"count 1", "", false, []float64{1, 0, 0, 0, 0, 0}},
    61  	})
    62  
    63  	js, err := mt.Data("12345", end)
    64  	if nil != err {
    65  		t.Error(err)
    66  	}
    67  	// The JSON metric order is not deterministic, so we merely test that it
    68  	// is valid JSON.
    69  	if err := isValidJSON(js); nil != err {
    70  		t.Error(err, string(js))
    71  	}
    72  }
    73  
    74  func TestApplyRules(t *testing.T) {
    75  	js := `[
    76  	{
    77  		"ignore":false,
    78  		"each_segment":false,
    79  		"terminate_chain":true,
    80  		"replacement":"been_renamed",
    81  		"replace_all":false,
    82  		"match_expression":"one$",
    83  		"eval_order":1
    84  	},
    85  	{
    86  		"ignore":true,
    87  		"each_segment":false,
    88  		"terminate_chain":true,
    89  		"replace_all":false,
    90  		"match_expression":"ignore_me",
    91  		"eval_order":1
    92  	},
    93  	{
    94  		"ignore":false,
    95  		"each_segment":false,
    96  		"terminate_chain":true,
    97  		"replacement":"merge_me",
    98  		"replace_all":false,
    99  		"match_expression":"merge_me[0-9]+$",
   100  		"eval_order":1
   101  	}
   102  	]`
   103  	var rules metricRules
   104  	err := json.Unmarshal([]byte(js), &rules)
   105  	if nil != err {
   106  		t.Fatal(err)
   107  	}
   108  
   109  	mt := newMetricTable(20, start)
   110  	mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
   111  	mt.addDuration("one", "scope1", 2*time.Second, 1*time.Second, unforced)
   112  	mt.addDuration("one", "scope2", 2*time.Second, 1*time.Second, unforced)
   113  	mt.addDuration("ignore_me", "", 2*time.Second, 1*time.Second, unforced)
   114  	mt.addDuration("ignore_me", "scope1", 2*time.Second, 1*time.Second, unforced)
   115  	mt.addDuration("ignore_me", "scope2", 2*time.Second, 1*time.Second, unforced)
   116  	mt.addDuration("merge_me1", "", 2*time.Second, 1*time.Second, unforced)
   117  	mt.addDuration("merge_me2", "", 2*time.Second, 1*time.Second, unforced)
   118  
   119  	applied := mt.ApplyRules(rules)
   120  	ExpectMetrics(t, applied, []WantMetric{
   121  		{"been_renamed", "", false, []float64{1, 2, 1, 2, 2, 4}},
   122  		{"been_renamed", "scope1", false, []float64{1, 2, 1, 2, 2, 4}},
   123  		{"been_renamed", "scope2", false, []float64{1, 2, 1, 2, 2, 4}},
   124  		{"merge_me", "", false, []float64{2, 4, 2, 2, 2, 8}},
   125  	})
   126  }
   127  
   128  func TestApplyEmptyRules(t *testing.T) {
   129  	js := `[]`
   130  	var rules metricRules
   131  	err := json.Unmarshal([]byte(js), &rules)
   132  	if nil != err {
   133  		t.Fatal(err)
   134  	}
   135  	mt := newMetricTable(20, start)
   136  	mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
   137  	mt.addDuration("one", "my_scope", 2*time.Second, 1*time.Second, unforced)
   138  	applied := mt.ApplyRules(rules)
   139  	ExpectMetrics(t, applied, []WantMetric{
   140  		{"one", "", false, []float64{1, 2, 1, 2, 2, 4}},
   141  		{"one", "my_scope", false, []float64{1, 2, 1, 2, 2, 4}},
   142  	})
   143  }
   144  
   145  func TestApplyNilRules(t *testing.T) {
   146  	var rules metricRules
   147  
   148  	mt := newMetricTable(20, start)
   149  	mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
   150  	mt.addDuration("one", "my_scope", 2*time.Second, 1*time.Second, unforced)
   151  	applied := mt.ApplyRules(rules)
   152  	ExpectMetrics(t, applied, []WantMetric{
   153  		{"one", "", false, []float64{1, 2, 1, 2, 2, 4}},
   154  		{"one", "my_scope", false, []float64{1, 2, 1, 2, 2, 4}},
   155  	})
   156  }
   157  
   158  func TestForced(t *testing.T) {
   159  	mt := newMetricTable(0, start)
   160  
   161  	mt.addDuration("unforced", "", 1*time.Second, 1*time.Second, unforced)
   162  	mt.addDuration("forced", "", 2*time.Second, 2*time.Second, forced)
   163  
   164  	ExpectMetrics(t, mt, []WantMetric{
   165  		{"forced", "", true, []float64{1, 2, 2, 2, 2, 4}},
   166  		{supportabilityDropped, "", true, []float64{1, 0, 0, 0, 0, 0}},
   167  	})
   168  
   169  }
   170  
   171  func TestMetricsMergeIntoEmpty(t *testing.T) {
   172  	src := newMetricTable(20, start)
   173  	src.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
   174  	src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   175  	dest := newMetricTable(20, start)
   176  	dest.merge(src, "")
   177  
   178  	ExpectMetrics(t, dest, []WantMetric{
   179  		{"one", "", false, []float64{1, 2, 1, 2, 2, 4}},
   180  		{"two", "", false, []float64{1, 2, 1, 2, 2, 4}},
   181  	})
   182  }
   183  
   184  func TestMetricsMergeFromEmpty(t *testing.T) {
   185  	src := newMetricTable(20, start)
   186  	dest := newMetricTable(20, start)
   187  	dest.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
   188  	dest.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   189  	dest.merge(src, "")
   190  
   191  	ExpectMetrics(t, dest, []WantMetric{
   192  		{"one", "", false, []float64{1, 2, 1, 2, 2, 4}},
   193  		{"two", "", false, []float64{1, 2, 1, 2, 2, 4}},
   194  	})
   195  }
   196  
   197  func TestMetricsMerge(t *testing.T) {
   198  	src := newMetricTable(20, start)
   199  	dest := newMetricTable(20, start)
   200  	dest.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
   201  	dest.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   202  	src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   203  	src.addDuration("three", "", 2*time.Second, 1*time.Second, unforced)
   204  
   205  	dest.merge(src, "")
   206  
   207  	ExpectMetrics(t, dest, []WantMetric{
   208  		{"one", "", false, []float64{1, 2, 1, 2, 2, 4}},
   209  		{"two", "", false, []float64{2, 4, 2, 2, 2, 8}},
   210  		{"three", "", false, []float64{1, 2, 1, 2, 2, 4}},
   211  	})
   212  }
   213  
   214  func TestMergeFailedSuccess(t *testing.T) {
   215  	src := newMetricTable(20, start)
   216  	dest := newMetricTable(20, end)
   217  	dest.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
   218  	dest.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   219  	src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   220  	src.addDuration("three", "", 2*time.Second, 1*time.Second, unforced)
   221  
   222  	if 0 != dest.failedHarvests {
   223  		t.Fatal(dest.failedHarvests)
   224  	}
   225  
   226  	dest.mergeFailed(src)
   227  
   228  	ExpectMetrics(t, dest, []WantMetric{
   229  		{"one", "", false, []float64{1, 2, 1, 2, 2, 4}},
   230  		{"two", "", false, []float64{2, 4, 2, 2, 2, 8}},
   231  		{"three", "", false, []float64{1, 2, 1, 2, 2, 4}},
   232  	})
   233  }
   234  
   235  func TestMergeFailedLimitReached(t *testing.T) {
   236  	src := newMetricTable(20, start)
   237  	dest := newMetricTable(20, end)
   238  	dest.addDuration("one", "", 2*time.Second, 1*time.Second, unforced)
   239  	dest.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   240  	src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   241  	src.addDuration("three", "", 2*time.Second, 1*time.Second, unforced)
   242  
   243  	src.failedHarvests = failedMetricAttemptsLimit
   244  
   245  	dest.mergeFailed(src)
   246  
   247  	ExpectMetrics(t, dest, []WantMetric{
   248  		{"one", "", false, []float64{1, 2, 1, 2, 2, 4}},
   249  		{"two", "", false, []float64{1, 2, 1, 2, 2, 4}},
   250  	})
   251  }
   252  
   253  func BenchmarkMetricTableCollectorJSON(b *testing.B) {
   254  	mt := newMetricTable(2000, time.Now())
   255  	md := metricData{
   256  		countSatisfied:  1234567812345678.1234567812345678,
   257  		totalTolerated:  1234567812345678.1234567812345678,
   258  		exclusiveFailed: 1234567812345678.1234567812345678,
   259  		min:             1234567812345678.1234567812345678,
   260  		max:             1234567812345678.1234567812345678,
   261  		sumSquares:      1234567812345678.1234567812345678,
   262  	}
   263  
   264  	for i := 0; i < 20; i++ {
   265  		scope := fmt.Sprintf("WebTransaction/Uri/myblog2/%d", i)
   266  
   267  		for j := 0; j < 20; j++ {
   268  			name := fmt.Sprintf("Datastore/statement/MySQL/City%d/insert", j)
   269  			mt.add(name, "", md, forced)
   270  			mt.add(name, scope, md, forced)
   271  
   272  			name = fmt.Sprintf("WebTransaction/Uri/myblog2/newPost_rum_%d.php", j)
   273  			mt.add(name, "", md, forced)
   274  			mt.add(name, scope, md, forced)
   275  		}
   276  	}
   277  
   278  	data, err := mt.CollectorJSON("12345", time.Now())
   279  	if nil != err {
   280  		b.Fatal(err)
   281  	}
   282  	if err := isValidJSON(data); nil != err {
   283  		b.Fatal(err, string(data))
   284  	}
   285  
   286  	b.ResetTimer()
   287  	b.ReportAllocs()
   288  
   289  	id := "12345"
   290  	now := time.Now()
   291  	for i := 0; i < b.N; i++ {
   292  		mt.CollectorJSON(id, now)
   293  	}
   294  }
   295  
   296  func BenchmarkAddingSameMetrics(b *testing.B) {
   297  	name := "my_name"
   298  	scope := "my_scope"
   299  	duration := 2 * time.Second
   300  	exclusive := 1 * time.Second
   301  
   302  	mt := newMetricTable(2000, time.Now())
   303  
   304  	b.ResetTimer()
   305  	b.ReportAllocs()
   306  
   307  	for i := 0; i < b.N; i++ {
   308  		mt.addDuration(name, scope, duration, exclusive, forced)
   309  		mt.addSingleCount(name, forced)
   310  	}
   311  }
   312  
   313  func TestMergedMetricsAreCopied(t *testing.T) {
   314  	src := newMetricTable(20, start)
   315  	dest := newMetricTable(20, start)
   316  
   317  	src.addSingleCount("zip", unforced)
   318  	dest.merge(src, "")
   319  	src.addSingleCount("zip", unforced)
   320  	ExpectMetrics(t, dest, []WantMetric{
   321  		{"zip", "", false, []float64{1, 0, 0, 0, 0, 0}},
   322  	})
   323  }
   324  
   325  func TestMergedWithScope(t *testing.T) {
   326  	src := newMetricTable(20, start)
   327  	dest := newMetricTable(20, start)
   328  
   329  	src.addSingleCount("one", unforced)
   330  	src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced)
   331  	dest.addDuration("two", "my_scope", 2*time.Second, 1*time.Second, unforced)
   332  	dest.merge(src, "my_scope")
   333  
   334  	ExpectMetrics(t, dest, []WantMetric{
   335  		{"one", "my_scope", false, []float64{1, 0, 0, 0, 0, 0}},
   336  		{"two", "my_scope", false, []float64{2, 4, 2, 2, 2, 8}},
   337  	})
   338  }