github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/ingester/active_series_test.go (about)

     1  package ingester
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math"
     7  	"strconv"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/prometheus/prometheus/pkg/labels"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/cortexproject/cortex/pkg/ingester/client"
    17  )
    18  
    19  func copyFn(l labels.Labels) labels.Labels { return l }
    20  
    21  func TestActiveSeries_UpdateSeries(t *testing.T) {
    22  	ls1 := []labels.Label{{Name: "a", Value: "1"}}
    23  	ls2 := []labels.Label{{Name: "a", Value: "2"}}
    24  
    25  	c := NewActiveSeries()
    26  	assert.Equal(t, 0, c.Active())
    27  
    28  	c.UpdateSeries(ls1, time.Now(), copyFn)
    29  	assert.Equal(t, 1, c.Active())
    30  
    31  	c.UpdateSeries(ls1, time.Now(), copyFn)
    32  	assert.Equal(t, 1, c.Active())
    33  
    34  	c.UpdateSeries(ls2, time.Now(), copyFn)
    35  	assert.Equal(t, 2, c.Active())
    36  }
    37  
    38  func TestActiveSeries_ShouldCorrectlyHandleFingerprintCollisions(t *testing.T) {
    39  	metric := labels.NewBuilder(labels.FromStrings("__name__", "logs"))
    40  	ls1 := metric.Set("_", "ypfajYg2lsv").Labels()
    41  	ls2 := metric.Set("_", "KiqbryhzUpn").Labels()
    42  
    43  	require.True(t, client.Fingerprint(ls1) == client.Fingerprint(ls2))
    44  
    45  	c := NewActiveSeries()
    46  	c.UpdateSeries(ls1, time.Now(), copyFn)
    47  	c.UpdateSeries(ls2, time.Now(), copyFn)
    48  
    49  	assert.Equal(t, 2, c.Active())
    50  }
    51  
    52  func TestActiveSeries_Purge(t *testing.T) {
    53  	series := [][]labels.Label{
    54  		{{Name: "a", Value: "1"}},
    55  		{{Name: "a", Value: "2"}},
    56  		// The two following series have the same Fingerprint
    57  		{{Name: "_", Value: "ypfajYg2lsv"}, {Name: "__name__", Value: "logs"}},
    58  		{{Name: "_", Value: "KiqbryhzUpn"}, {Name: "__name__", Value: "logs"}},
    59  	}
    60  
    61  	// Run the same test for increasing TTL values
    62  	for ttl := 0; ttl < len(series); ttl++ {
    63  		c := NewActiveSeries()
    64  
    65  		for i := 0; i < len(series); i++ {
    66  			c.UpdateSeries(series[i], time.Unix(int64(i), 0), copyFn)
    67  		}
    68  
    69  		c.Purge(time.Unix(int64(ttl+1), 0))
    70  		// call purge twice, just to hit "quick" path. It doesn't really do anything.
    71  		c.Purge(time.Unix(int64(ttl+1), 0))
    72  
    73  		exp := len(series) - (ttl + 1)
    74  		assert.Equal(t, exp, c.Active())
    75  	}
    76  }
    77  
    78  func TestActiveSeries_PurgeOpt(t *testing.T) {
    79  	metric := labels.NewBuilder(labels.FromStrings("__name__", "logs"))
    80  	ls1 := metric.Set("_", "ypfajYg2lsv").Labels()
    81  	ls2 := metric.Set("_", "KiqbryhzUpn").Labels()
    82  
    83  	c := NewActiveSeries()
    84  
    85  	now := time.Now()
    86  	c.UpdateSeries(ls1, now.Add(-2*time.Minute), copyFn)
    87  	c.UpdateSeries(ls2, now, copyFn)
    88  	c.Purge(now)
    89  
    90  	assert.Equal(t, 1, c.Active())
    91  
    92  	c.UpdateSeries(ls1, now.Add(-1*time.Minute), copyFn)
    93  	c.UpdateSeries(ls2, now, copyFn)
    94  	c.Purge(now)
    95  
    96  	assert.Equal(t, 1, c.Active())
    97  
    98  	// This will *not* update the series, since there is already newer timestamp.
    99  	c.UpdateSeries(ls2, now.Add(-1*time.Minute), copyFn)
   100  	c.Purge(now)
   101  
   102  	assert.Equal(t, 1, c.Active())
   103  }
   104  
   105  var activeSeriesTestGoroutines = []int{50, 100, 500}
   106  
   107  func BenchmarkActiveSeriesTest_single_series(b *testing.B) {
   108  	for _, num := range activeSeriesTestGoroutines {
   109  		b.Run(fmt.Sprintf("%d", num), func(b *testing.B) {
   110  			benchmarkActiveSeriesConcurrencySingleSeries(b, num)
   111  		})
   112  	}
   113  }
   114  
   115  func benchmarkActiveSeriesConcurrencySingleSeries(b *testing.B, goroutines int) {
   116  	series := labels.Labels{
   117  		{Name: "a", Value: "a"},
   118  	}
   119  
   120  	c := NewActiveSeries()
   121  
   122  	wg := &sync.WaitGroup{}
   123  	start := make(chan struct{})
   124  	max := int(math.Ceil(float64(b.N) / float64(goroutines)))
   125  
   126  	for i := 0; i < goroutines; i++ {
   127  		wg.Add(1)
   128  		go func() {
   129  			defer wg.Done()
   130  			<-start
   131  
   132  			now := time.Now()
   133  
   134  			for ix := 0; ix < max; ix++ {
   135  				now = now.Add(time.Duration(ix) * time.Millisecond)
   136  				c.UpdateSeries(series, now, copyFn)
   137  			}
   138  		}()
   139  	}
   140  
   141  	b.ResetTimer()
   142  	close(start)
   143  	wg.Wait()
   144  }
   145  
   146  func BenchmarkActiveSeries_UpdateSeries(b *testing.B) {
   147  	c := NewActiveSeries()
   148  
   149  	// Prepare series
   150  	nameBuf := bytes.Buffer{}
   151  	for i := 0; i < 50; i++ {
   152  		nameBuf.WriteString("abcdefghijklmnopqrstuvzyx")
   153  	}
   154  	name := nameBuf.String()
   155  
   156  	series := make([]labels.Labels, b.N)
   157  	for s := 0; s < b.N; s++ {
   158  		series[s] = labels.Labels{{Name: name, Value: name + strconv.Itoa(s)}}
   159  	}
   160  
   161  	now := time.Now().UnixNano()
   162  
   163  	b.ResetTimer()
   164  	for ix := 0; ix < b.N; ix++ {
   165  		c.UpdateSeries(series[ix], time.Unix(0, now+int64(ix)), copyFn)
   166  	}
   167  }
   168  
   169  func BenchmarkActiveSeries_Purge_once(b *testing.B) {
   170  	benchmarkPurge(b, false)
   171  }
   172  
   173  func BenchmarkActiveSeries_Purge_twice(b *testing.B) {
   174  	benchmarkPurge(b, true)
   175  }
   176  
   177  func benchmarkPurge(b *testing.B, twice bool) {
   178  	const numSeries = 10000
   179  	const numExpiresSeries = numSeries / 25
   180  
   181  	now := time.Now()
   182  	c := NewActiveSeries()
   183  
   184  	series := [numSeries]labels.Labels{}
   185  	for s := 0; s < numSeries; s++ {
   186  		series[s] = labels.Labels{{Name: "a", Value: strconv.Itoa(s)}}
   187  	}
   188  
   189  	for i := 0; i < b.N; i++ {
   190  		b.StopTimer()
   191  
   192  		// Prepare series
   193  		for ix, s := range series {
   194  			if ix < numExpiresSeries {
   195  				c.UpdateSeries(s, now.Add(-time.Minute), copyFn)
   196  			} else {
   197  				c.UpdateSeries(s, now, copyFn)
   198  			}
   199  		}
   200  
   201  		assert.Equal(b, numSeries, c.Active())
   202  		b.StartTimer()
   203  
   204  		// Purge everything
   205  		c.Purge(now)
   206  		assert.Equal(b, numSeries-numExpiresSeries, c.Active())
   207  
   208  		if twice {
   209  			c.Purge(now)
   210  			assert.Equal(b, numSeries-numExpiresSeries, c.Active())
   211  		}
   212  	}
   213  }