github.com/blend/go-sdk@v1.20220411.3/autoflush/buffer_test.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package autoflush 9 10 import ( 11 "context" 12 "fmt" 13 "sync" 14 "sync/atomic" 15 "testing" 16 "time" 17 18 "github.com/blend/go-sdk/assert" 19 "github.com/blend/go-sdk/graceful" 20 "github.com/blend/go-sdk/stats" 21 ) 22 23 // Assert Buffer is graceful. 24 var ( 25 _ graceful.Graceful = (*Buffer)(nil) 26 ) 27 28 func Test_Buffer_MaxLen(t *testing.T) { 29 assert := assert.New(t) 30 31 wg := sync.WaitGroup{} 32 wg.Add(2) 33 34 var processed int32 35 handler := func(_ context.Context, objects []interface{}) error { 36 defer wg.Done() 37 atomic.AddInt32(&processed, int32(len(objects))) 38 return nil 39 } 40 41 afb := New(handler, 42 OptMaxLen(10), 43 OptInterval(time.Hour), 44 ) 45 46 go func() { _ = afb.Start() }() 47 <-afb.NotifyStarted() 48 defer func() { _ = afb.Stop() }() 49 50 for x := 0; x < 20; x++ { 51 afb.Add(context.TODO(), fmt.Sprintf("foo%d", x)) 52 } 53 54 wg.Wait() 55 assert.Equal(20, processed) 56 } 57 58 func Test_Buffer_Ticker(t *testing.T) { 59 assert := assert.New(t) 60 61 wg := sync.WaitGroup{} 62 wg.Add(20) 63 buffer := New(func(_ context.Context, objects []interface{}) error { 64 for range objects { 65 wg.Done() 66 } 67 return nil 68 }, OptMaxLen(100), OptInterval(time.Millisecond)) 69 70 go func() { _ = buffer.Start() }() 71 <-buffer.NotifyStarted() 72 defer func() { _ = buffer.Stop() }() 73 74 for x := 0; x < 20; x++ { 75 buffer.Add(context.TODO(), fmt.Sprintf("foo%d", x)) 76 } 77 wg.Wait() 78 assert.True(true) 79 } 80 81 func Test_Buffer_Stop(t *testing.T) { 82 assert := assert.New(t) 83 84 wg := sync.WaitGroup{} 85 wg.Add(20) 86 handler := func(_ context.Context, objects []interface{}) error { 87 for range objects { 88 wg.Done() 89 } 90 return nil 91 } 92 93 buffer := New(handler, 94 OptMaxLen(10), 95 OptInterval(time.Hour), 96 OptShutdownGracePeriod(5*time.Second), 97 ) 98 99 go func() { _ = buffer.Start() }() 100 <-buffer.NotifyStarted() 101 102 for x := 0; x < 20; x++ { 103 buffer.Add(context.TODO(), fmt.Sprintf("foo%d", x)) 104 } 105 106 assert.Nil(buffer.Stop()) 107 assert.True(buffer.Latch.IsStopped()) 108 wg.Wait() 109 } 110 111 func Test_Buffer_Stats(t *testing.T) { 112 assert := assert.New(t) 113 todo := context.TODO() 114 115 mockCollector := stats.NewMockCollector(128) 116 117 maxItems := 10 118 numFlushes := 5 119 120 var msg struct{} 121 var flushBlock []interface{} 122 for x := 0; x < maxItems; x++ { 123 flushBlock = append(flushBlock, msg) 124 } 125 126 afb := New(nil, 127 OptMaxLen(maxItems), 128 OptStats(mockCollector), 129 ) 130 assert.Equal(maxItems, afb.contents.Capacity()) 131 afb.flushes = make(chan Flush, maxItems) 132 133 for x := 0; x < numFlushes; x++ { 134 afb.AddMany(todo, flushBlock...) // should cause a queued flush 135 } 136 137 assert.Equal(numFlushes, len(afb.flushes)) 138 metrics := mockCollector.AllMetrics() 139 140 assert.AnyCount(metrics, numFlushes, func(v interface{}) bool { 141 typed := v.(stats.MockMetric) 142 return typed.Name == MetricBufferLength 143 }, MetricBufferLength) 144 assert.AnyCount(metrics, numFlushes, func(v interface{}) bool { 145 typed := v.(stats.MockMetric) 146 return typed.Name == MetricFlush && typed.Count == 1 147 }, MetricFlush) 148 assert.AnyCount(metrics, numFlushes*3, func(v interface{}) bool { 149 typed := v.(stats.MockMetric) 150 return typed.Name == MetricFlushEnqueueElapsed 151 }, MetricFlushEnqueueElapsed) 152 153 assert.AnyCount(metrics, 1, func(v interface{}) bool { 154 typed := v.(stats.MockMetric) 155 return typed.Name == MetricFlushQueueLength && typed.Gauge == 0.0 156 }) 157 assert.AnyCount(metrics, 1, func(v interface{}) bool { 158 typed := v.(stats.MockMetric) 159 return typed.Name == MetricFlushQueueLength && typed.Gauge == 1.0 160 }) 161 assert.AnyCount(metrics, 1, func(v interface{}) bool { 162 typed := v.(stats.MockMetric) 163 return typed.Name == MetricFlushQueueLength && typed.Gauge == 2.0 164 }) 165 assert.AnyCount(metrics, 1, func(v interface{}) bool { 166 typed := v.(stats.MockMetric) 167 return typed.Name == MetricFlushQueueLength && typed.Gauge == 3.0 168 }) 169 assert.AnyCount(metrics, 1, func(v interface{}) bool { 170 typed := v.(stats.MockMetric) 171 return typed.Name == MetricFlushQueueLength && typed.Gauge == 4.0 172 }) 173 } 174 175 func BenchmarkBuffer(b *testing.B) { 176 buffer := New(func(_ context.Context, objects []interface{}) error { 177 if len(objects) > 128 { 178 b.Fail() 179 } 180 return nil 181 }, OptMaxLen(128), OptInterval(500*time.Millisecond)) 182 183 go func() { _ = buffer.Start() }() 184 <-buffer.NotifyStarted() 185 defer func() { _ = buffer.Stop() }() 186 187 for x := 0; x < b.N; x++ { 188 for y := 0; y < 1000; y++ { 189 buffer.Add(context.TODO(), fmt.Sprintf("asdf%d%d", x, y)) 190 } 191 } 192 }