github.com/GuanceCloud/cliutils@v1.1.21/diskcache/put_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 "errors" 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 func BenchmarkNosyncPutGet(b *T.B) { 22 reg := prometheus.NewRegistry() 23 register(reg) 24 25 p := b.TempDir() 26 c, err := Open(WithPath(p), WithNoSync(true), WithBatchSize(1024*1024*4), WithCapacity(4*1024*1024*1024)) 27 require.NoError(b, err) 28 29 _1mb := make([]byte, 1024*1024) 30 _1kb := make([]byte, 1024) 31 _512kb := make([]byte, 1024*512) 32 33 b.Run(`put-1mb`, func(b *T.B) { 34 for i := 0; i < b.N; i++ { 35 c.Put(_1mb) 36 } 37 }) 38 39 b.Run(`put-1kb`, func(b *T.B) { 40 for i := 0; i < b.N; i++ { 41 c.Put(_1kb) 42 } 43 }) 44 45 b.Run(`put-512kb`, func(b *T.B) { 46 for i := 0; i < b.N; i++ { 47 c.Put(_512kb) 48 } 49 }) 50 51 b.Run(`get`, func(b *T.B) { 52 for i := 0; i < b.N; i++ { 53 c.Get(func(_ []byte) error { return nil }) 54 } 55 }) 56 57 mfs, err := reg.Gather() 58 require.NoError(b, err) 59 b.Logf("\n%s", metrics.MetricFamily2Text(mfs)) 60 61 b.Cleanup(func() { 62 assert.NoError(b, c.Close()) 63 ResetMetrics() 64 }) 65 } 66 67 func BenchmarkPutGet(b *T.B) { 68 reg := prometheus.NewRegistry() 69 register(reg) 70 p := b.TempDir() 71 c, err := Open(WithPath(p), WithBatchSize(1024*1024*4), WithCapacity(4*1024*1024*1024)) 72 require.NoError(b, err) 73 74 _1mb := make([]byte, 1024*1024) 75 _1kb := make([]byte, 1024) 76 _512kb := make([]byte, 1024*512) 77 78 b.Run(`put-1mb`, func(b *T.B) { 79 for i := 0; i < b.N; i++ { 80 c.Put(_1mb) 81 } 82 }) 83 84 b.Run(`put-1kb`, func(b *T.B) { 85 for i := 0; i < b.N; i++ { 86 c.Put(_1kb) 87 } 88 }) 89 90 b.Run(`put-512kb`, func(b *T.B) { 91 for i := 0; i < b.N; i++ { 92 c.Put(_512kb) 93 } 94 }) 95 96 b.Run(`get`, func(b *T.B) { 97 for i := 0; i < b.N; i++ { 98 c.Get(func(_ []byte) error { return nil }) 99 } 100 }) 101 102 mfs, err := reg.Gather() 103 require.NoError(b, err) 104 105 b.Logf("\n%s", metrics.MetricFamily2Text(mfs)) 106 107 b.Cleanup(func() { 108 assert.NoError(b, c.Close()) 109 ResetMetrics() 110 }) 111 } 112 113 func TestConcurrentPutGet(t *T.T) { 114 var ( 115 p = t.TempDir() 116 mb = int64(1024 * 1024) 117 sample = make([]byte, 5*7351) 118 eof = 0 119 reg = prometheus.NewRegistry() 120 ) 121 122 register(reg) 123 124 c, err := Open(WithPath(p), WithBatchSize(4*mb), WithCapacity(128*mb)) 125 assert.NoError(t, err) 126 127 defer c.Close() 128 129 wg := sync.WaitGroup{} 130 concurrency := 4 131 132 fnPut := func(idx int) { 133 defer wg.Done() 134 nput := 0 135 exceed100ms := 0 136 137 for { 138 start := time.Now() 139 assert.NoError(t, c.Put(sample)) 140 141 cost := time.Since(start) 142 if cost > 100*time.Millisecond { 143 exceed100ms++ 144 } 145 146 nput++ 147 148 if nput > 1000 { 149 t.Logf("[%d] Put exit", idx) 150 return 151 } 152 } 153 } 154 155 wg.Add(concurrency) 156 for i := 0; i < concurrency; i++ { 157 go fnPut(i) 158 } 159 160 fnGet := func(idx int) { 161 defer wg.Done() 162 nget := 0 163 readBytes := 0 164 exceed100ms := 0 165 166 for { 167 nget++ 168 start := time.Now() 169 if err := c.Get(func(x []byte) error { 170 assert.Equal(t, sample, x) 171 readBytes += len(x) 172 173 cost := time.Since(start) 174 if cost > 100*time.Millisecond { 175 exceed100ms++ 176 } 177 178 return nil 179 }); err != nil { 180 if errors.Is(err, ErrEOF) { 181 time.Sleep(time.Second) 182 eof++ 183 if eof >= 10 { 184 break 185 } 186 } else { 187 t.Logf("[%d]: %s", idx, err) 188 time.Sleep(time.Second) 189 } 190 } else { 191 eof = 0 // reset eof if Get ok 192 } 193 } 194 } 195 196 wg.Add(concurrency) 197 for i := 0; i < concurrency; i++ { 198 go fnGet(i) 199 } 200 201 wg.Wait() 202 203 mfs, err := reg.Gather() 204 require.NoError(t, err) 205 t.Logf("\n%s", metrics.MetricFamily2Text(mfs)) 206 207 t.Cleanup(func() { 208 assert.NoError(t, c.Close()) 209 ResetMetrics() 210 }) 211 } 212 213 func TestPutOnCapacityReached(t *T.T) { 214 t.Run(`reach-capacity-single-put`, func(t *T.T) { 215 var ( 216 mb = int64(1024 * 1024) 217 p = t.TempDir() 218 capacity = 32 * mb 219 large = make([]byte, mb) 220 small = make([]byte, 1024*3) 221 maxPut = 4 * capacity 222 ) 223 224 reg := prometheus.NewRegistry() 225 register(reg) 226 227 t.Logf("path: %s", p) 228 229 c, err := Open(WithPath(p), WithCapacity(capacity), WithBatchSize(4*mb)) 230 assert.NoError(t, err) 231 232 putBytes := 0 233 234 n := 0 235 for { 236 switch n % 2 { 237 case 0: 238 c.Put(small) 239 putBytes += len(small) 240 case 1: 241 c.Put(large) 242 putBytes += len(large) 243 } 244 n++ 245 246 if int64(putBytes) > maxPut { 247 break 248 } 249 } 250 251 mfs, err := reg.Gather() 252 require.NoError(t, err) 253 254 m := metrics.GetMetricOnLabels(mfs, 255 "diskcache_dropped_total", 256 c.path, 257 reasonExceedCapacity) 258 259 require.NotNil(t, m, "got metrics:\n%s", metrics.MetricFamily2Text(mfs)) 260 assert.True(t, m.GetCounter().GetValue() > 0.0) 261 262 t.Cleanup(func() { 263 require.NoError(t, c.Close()) 264 ResetMetrics() 265 }) 266 }) 267 268 t.Run(`reach-capacity-concurrent-put`, func(t *T.T) { 269 var ( 270 mb = int64(1024 * 1024) 271 p = t.TempDir() 272 capacity = 128 * mb 273 large = make([]byte, mb) 274 small = make([]byte, 1024*3) 275 maxPut = 4 * capacity 276 wg sync.WaitGroup 277 ) 278 279 reg := prometheus.NewRegistry() 280 register(reg) 281 282 t.Logf("path: %s", p) 283 284 c, err := Open(WithPath(p), WithCapacity(capacity), WithBatchSize(4*mb)) 285 assert.NoError(t, err) 286 287 total := int64(0) 288 289 wg.Add(10) 290 for i := 0; i < 10; i++ { 291 go func() { 292 defer wg.Done() 293 n := 0 294 for { 295 switch n % 2 { 296 case 0: 297 c.Put(small) 298 atomic.AddInt64(&total, 1024*3) 299 case 1: 300 c.Put(large) 301 atomic.AddInt64(&total, mb) 302 } 303 n++ 304 305 if atomic.LoadInt64(&total) > maxPut { 306 return 307 } 308 } 309 }() 310 } 311 312 wg.Wait() 313 314 mfs, err := reg.Gather() 315 require.NoError(t, err) 316 317 m := metrics.GetMetricOnLabels(mfs, 318 "diskcache_dropped_total", 319 c.path, 320 reasonExceedCapacity) 321 require.NotNil(t, m, "got metrics:\n%s", metrics.MetricFamily2Text(mfs)) 322 323 assert.True(t, m.GetCounter().GetValue() > 0.0) 324 325 t.Cleanup(func() { 326 assert.NoError(t, c.Close()) 327 ResetMetrics() 328 }) 329 }) 330 }