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 }