github.com/GuanceCloud/cliutils@v1.1.21/diskcache/metric_test.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package diskcache
     7  
     8  import (
     9  	"math/rand"
    10  	"sync"
    11  	"sync/atomic"
    12  	T "testing"
    13  	"time"
    14  
    15  	"github.com/GuanceCloud/cliutils/metrics"
    16  	"github.com/prometheus/client_golang/prometheus"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  // get random bytes from data
    22  func getSamples(data []byte) []byte {
    23  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
    24  
    25  	// at least 1/10 of data
    26  	n := (len(data)/10 + (r.Int() % len(data)))
    27  	if n >= len(data) {
    28  		n = len(data)
    29  	}
    30  
    31  	start := r.Int() % len(data)
    32  
    33  	if start+n > len(data) {
    34  		return data[len(data)-n:] // return last n bytes
    35  	} else {
    36  		return data[start : start+n]
    37  	}
    38  }
    39  
    40  func TestPutGetMetrics(t *T.T) {
    41  	t.Run("test-wakeup-count", func(t *T.T) {
    42  		reg := prometheus.NewRegistry()
    43  		register(reg)
    44  
    45  		p := t.TempDir()
    46  		c, err := Open(WithPath(p), WithWakeup(time.Second))
    47  		require.NoError(t, err)
    48  
    49  		data := make([]byte, 4)
    50  
    51  		require.NoError(t, c.Put(data))
    52  
    53  		time.Sleep(time.Second * 2)
    54  
    55  		require.NoError(t, c.Get(nil))
    56  
    57  		mfs, err := reg.Gather()
    58  		assert.NoError(t, err)
    59  
    60  		m := metrics.GetMetricOnLabels(mfs, "diskcache_wakeup_total", c.path)
    61  		require.NotNil(t, m)
    62  		require.Equal(t, 1.0, m.GetCounter().GetValue())
    63  
    64  		m = metrics.GetMetricOnLabels(mfs, "diskcache_rotate_total", c.path)
    65  		require.NotNil(t, m)
    66  		require.Equal(t, 1.0, m.GetCounter().GetValue())
    67  
    68  		t.Cleanup(func() {
    69  			ResetMetrics()
    70  			assert.NoError(t, c.Close())
    71  		})
    72  	})
    73  
    74  	// test if metric size ok
    75  	t.Run("metrics-on-only-put", func(t *T.T) {
    76  		reg := prometheus.NewRegistry()
    77  		register(reg)
    78  
    79  		p := t.TempDir()
    80  		bsize := int64(100)
    81  
    82  		c, err := Open(
    83  			WithPath(p),
    84  			WithBatchSize(bsize),
    85  		)
    86  		require.NoError(t, err)
    87  
    88  		data := make([]byte, bsize/2)
    89  
    90  		totalPut := 0
    91  		for i := 0; i < 10; i++ {
    92  			c.Put(data)
    93  			totalPut += (len(data) + dataHeaderLen)
    94  		}
    95  
    96  		// check if size == totalPut
    97  		mfs, err := reg.Gather()
    98  		require.NoError(t, err)
    99  		m := metrics.GetMetricOnLabels(mfs, "diskcache_size", c.path)
   100  		require.NotNil(t, m)
   101  		got := int(m.GetGauge().GetValue())
   102  		assert.Equal(t, totalPut, got, "c.size: %d, size-expect=%d", c.size, got-totalPut)
   103  
   104  		t.Logf("metrics:\n%s", metrics.MetricFamily2Text(mfs))
   105  
   106  		t.Cleanup(func() {
   107  			ResetMetrics()
   108  			assert.NoError(t, c.Close())
   109  		})
   110  	})
   111  
   112  	t.Run("metrics-on-put-get", func(t *T.T) {
   113  		reg := prometheus.NewRegistry()
   114  		register(reg)
   115  
   116  		p := t.TempDir()
   117  		bsize := int64(100)
   118  
   119  		c, err := Open(
   120  			WithPath(p),
   121  			WithBatchSize(bsize),
   122  		)
   123  		require.NoError(t, err)
   124  
   125  		data := make([]byte, bsize/2)
   126  
   127  		totalPut := 0
   128  		for i := 0; i < 10; i++ {
   129  			c.Put(data)
   130  			totalPut += len(data) // without dataHeaderLen
   131  		}
   132  
   133  		// force rotate
   134  		assert.NoError(t, c.rotate())
   135  
   136  		totalGet := 0
   137  		for i := 0; i < 10; i++ {
   138  			c.Get(func(x []byte) error {
   139  				totalGet += len(x)
   140  				return nil
   141  			})
   142  		}
   143  
   144  		c.Get(nil) // read EOF to tiger remove
   145  
   146  		// check if size == totalPut
   147  		mfs, err := reg.Gather()
   148  		require.NoError(t, err)
   149  		m := metrics.GetMetricOnLabels(mfs, "diskcache_size", c.path)
   150  		require.NotNil(t, m)
   151  		got := int(m.GetGauge().GetValue())
   152  		assert.Equal(t, 0, got, "c.size: %d", c.size)
   153  
   154  		m = metrics.GetMetricOnLabels(mfs, "diskcache_get_bytes_total", c.path)
   155  		require.NotNil(t, m)
   156  		got = int(m.GetCounter().GetValue())
   157  		assert.Equal(t, totalGet, got)
   158  		assert.Equal(t, totalGet, totalPut)
   159  
   160  		m = metrics.GetMetricOnLabels(mfs, "diskcache_get_total", c.path)
   161  		require.NotNil(t, m)
   162  		got = int(m.GetCounter().GetValue())
   163  		assert.Equal(t, 10, got)
   164  
   165  		m = metrics.GetMetricOnLabels(mfs, "diskcache_get_latency", c.path)
   166  		require.NotNil(t, m)
   167  		got = int(m.GetSummary().GetSampleCount())
   168  		assert.Equal(t, 10, got)
   169  
   170  		m = metrics.GetMetricOnLabels(mfs, "diskcache_put_total", c.path)
   171  		require.NotNil(t, m)
   172  		got = int(m.GetCounter().GetValue())
   173  		assert.Equal(t, 10, got)
   174  
   175  		t.Cleanup(func() {
   176  			ResetMetrics()
   177  			assert.NoError(t, c.Close())
   178  		})
   179  	})
   180  }
   181  
   182  func TestMetric(t *T.T) {
   183  	t.Run("basic", func(t *T.T) {
   184  		p := t.TempDir()
   185  		c, err := Open(WithPath(p))
   186  		assert.NoError(t, err)
   187  
   188  		smallBytes := make([]byte, 100)
   189  
   190  		assert.NoError(t, c.Put(smallBytes))
   191  
   192  		mfs, err := metrics.Gather()
   193  		assert.NoError(t, err)
   194  
   195  		t.Logf("\n%s", metrics.MetricFamily2Text(mfs))
   196  
   197  		m := metrics.GetMetricOnLabels(mfs, "diskcache_put_total", c.path)
   198  		assert.Equal(t, float64(1), m.GetCounter().GetValue())
   199  
   200  		m = metrics.GetMetricOnLabels(mfs, "diskcache_put_bytes_total", c.path)
   201  		require.NotNil(t, m)
   202  		assert.Equal(t, float64(100), /* dataHeaderLen not counted in put_bytes */
   203  			m.GetCounter().GetValue())
   204  
   205  		m = metrics.GetMetricOnLabels(mfs, "diskcache_size", c.path)
   206  		require.NotNil(t, m)
   207  		assert.Equal(t, float64(104), /* dataHeaderLen counted in size */
   208  			m.GetGauge().GetValue())
   209  
   210  		// these fileds all zero
   211  		m = metrics.GetMetricOnLabels(mfs, "diskcache_dropped_batch", c.path)
   212  		require.Nil(t, m)
   213  
   214  		m = metrics.GetMetricOnLabels(mfs, "diskcache_get", c.path)
   215  		require.Nil(t, m)
   216  
   217  		m = metrics.GetMetricOnLabels(mfs, "diskcache_get_bytes_total", c.path)
   218  		require.Nil(t, m)
   219  
   220  		m = metrics.GetMetricOnLabels(mfs, "diskcache_get_latency", c.path)
   221  		require.Nil(t, m)
   222  
   223  		m = metrics.GetMetricOnLabels(mfs, "diskcache_rotate", c.path)
   224  		require.Nil(t, m)
   225  
   226  		// rotate to make it readble
   227  		assert.NoError(t, c.rotate())
   228  		assert.NoError(t, c.Get(nil))
   229  
   230  		mfs, err = metrics.Gather()
   231  		assert.NoError(t, err)
   232  		t.Logf("\n%s", metrics.MetricFamily2Text(mfs))
   233  
   234  		m = metrics.GetMetricOnLabels(mfs, "diskcache_get_total", c.path)
   235  		require.NotNil(t, m)
   236  		assert.Equal(t, float64(1), m.GetCounter().GetValue())
   237  
   238  		m = metrics.GetMetricOnLabels(mfs, "diskcache_get_bytes_total", c.path)
   239  		require.NotNil(t, m)
   240  		assert.Equal(t, float64(100), m.GetCounter().GetValue())
   241  
   242  		m = metrics.GetMetricOnLabels(mfs, "diskcache_size", c.path)
   243  		require.NotNil(t, m)
   244  		assert.Equal(t, float64(100+dataHeaderLen /*EOFHint*/), m.GetGauge().GetValue())
   245  
   246  		assert.NoError(t, c.Close())
   247  		mfs, err = metrics.Gather()
   248  		assert.NoError(t, err)
   249  		t.Logf("\n%s", metrics.MetricFamily2Text(mfs))
   250  
   251  		// check if open/close time metric exist.
   252  		m = metrics.GetMetricOnLabels(mfs, "diskcache_last_close_time", c.path)
   253  		require.NotNil(t, m)
   254  		m = metrics.GetMetricOnLabels(mfs, "diskcache_open_time", c.labels...)
   255  		require.NotNil(t, m)
   256  
   257  		t.Cleanup(func() {
   258  			ResetMetrics()
   259  		})
   260  	})
   261  }
   262  
   263  func TestConcurrentPutGetPerf(t *T.T) {
   264  	p := t.TempDir()
   265  	capacity := int64(1024 * 1024 * 1024)
   266  	data := make([]byte, 1024*1024)
   267  
   268  	t.Run("sing-put", func(t *T.T) {
   269  		c, err := Open(
   270  			WithPath(p),
   271  			WithCapacity(capacity),
   272  		)
   273  		require.NoError(t, err)
   274  
   275  		start := time.Now()
   276  		tick := time.NewTicker(time.Second)
   277  		defer tick.Stop()
   278  
   279  		var total int64
   280  		for {
   281  			select {
   282  			case <-tick.C:
   283  				goto end
   284  			default:
   285  				x := getSamples(data)
   286  				c.Put(x)
   287  				total += int64(len(x))
   288  			}
   289  		}
   290  
   291  	end:
   292  		t.Logf("write perf(%d bytes): %d bytes/ms", total, total/int64(time.Since(start)/time.Millisecond))
   293  
   294  		t.Cleanup(func() {
   295  			assert.NoError(t, c.Close())
   296  		})
   297  	})
   298  
   299  	t.Run("multi-put", func(t *T.T) {
   300  		c, err := Open(
   301  			WithPath(p),
   302  			WithCapacity(capacity),
   303  		)
   304  		require.NoError(t, err)
   305  
   306  		start := time.Now()
   307  		var total int64
   308  
   309  		tick := time.NewTicker(time.Second)
   310  		defer tick.Stop()
   311  
   312  		// 10 worker
   313  		wg := sync.WaitGroup{}
   314  		wg.Add(10)
   315  		for i := 0; i < 10; i++ {
   316  			go func() {
   317  				defer wg.Done()
   318  				for {
   319  					select {
   320  					case <-tick.C:
   321  						return
   322  					default:
   323  						x := getSamples(data)
   324  						c.Put(x)
   325  						atomic.AddInt64(&total, int64(len(x)))
   326  					}
   327  				}
   328  			}()
   329  		}
   330  
   331  		wg.Wait()
   332  
   333  		t.Logf("write perf(%d bytes): %d bytes/ms", total, total/int64(time.Since(start)/time.Millisecond))
   334  
   335  		t.Cleanup(func() {
   336  			assert.NoError(t, c.Close())
   337  		})
   338  	})
   339  
   340  	t.Run("multi-put-get", func(t *T.T) {
   341  		c, err := Open(
   342  			WithPath(p),
   343  			WithCapacity(capacity),
   344  		)
   345  		require.NoError(t, err)
   346  
   347  		start := time.Now()
   348  		var putTotal int64
   349  		var getTotal int64
   350  
   351  		tick := time.NewTicker(time.Second)
   352  		defer tick.Stop()
   353  
   354  		// put/get each 10 worker
   355  		wg := sync.WaitGroup{}
   356  		wg.Add(20)
   357  		for i := 0; i < 10; i++ {
   358  			go func() {
   359  				defer wg.Done()
   360  				for {
   361  					select {
   362  					case <-tick.C:
   363  						return
   364  					default:
   365  						x := getSamples(data)
   366  						c.Put(x)
   367  						atomic.AddInt64(&putTotal, int64(len(x)))
   368  					}
   369  				}
   370  			}()
   371  		}
   372  
   373  		for i := 0; i < 10; i++ {
   374  			go func() {
   375  				defer wg.Done()
   376  				for {
   377  					select {
   378  					case <-tick.C:
   379  						return
   380  					default:
   381  						if err := c.Get(func(x []byte) error {
   382  							atomic.AddInt64(&getTotal, int64(len(x)))
   383  							return nil
   384  						}); err != nil {
   385  							return
   386  						}
   387  					}
   388  				}
   389  			}()
   390  		}
   391  
   392  		wg.Wait()
   393  
   394  		t.Logf("write perf(%d bytes): put %d bytes/ms, get %dbytes/ms",
   395  			putTotal,
   396  			putTotal/int64(time.Since(start)/time.Millisecond),
   397  			getTotal/int64(time.Since(start)/time.Millisecond),
   398  		)
   399  
   400  		t.Cleanup(func() {
   401  			assert.NoError(t, c.Close())
   402  		})
   403  	})
   404  }