github.com/observiq/carbon@v0.9.11-0.20200820160507-1b872e368a5e/operator/buffer/memory_buffer_test.go (about) 1 package buffer 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/observiq/carbon/entry" 10 "github.com/observiq/carbon/operator" 11 "github.com/stretchr/testify/require" 12 "go.uber.org/zap" 13 "go.uber.org/zap/zaptest" 14 ) 15 16 type mockHandler struct { 17 fail chan bool 18 received chan []*entry.Entry 19 success chan []*entry.Entry 20 logger *zap.SugaredLogger 21 } 22 23 func (h *mockHandler) ProcessMulti(ctx context.Context, entries []*entry.Entry) error { 24 h.received <- entries 25 fail := <-h.fail 26 if fail { 27 return fmt.Errorf("test failure") 28 } 29 30 h.success <- entries 31 return nil 32 } 33 34 func (h *mockHandler) Logger() *zap.SugaredLogger { 35 return h.logger 36 } 37 38 func newMockHandler(t *testing.T) *mockHandler { 39 return &mockHandler{ 40 fail: make(chan bool), 41 received: make(chan []*entry.Entry), 42 success: make(chan []*entry.Entry), 43 logger: zaptest.NewLogger(t).Sugar(), 44 } 45 } 46 47 func TestMemoryBufferRetry(t *testing.T) { 48 t.Run("FailOnce", func(t *testing.T) { 49 cfg := NewConfig() 50 cfg.DelayThreshold = operator.Duration{Duration: 10 * time.Millisecond} 51 buffer, err := cfg.Build() 52 require.NoError(t, err) 53 handler := newMockHandler(t) 54 buffer.SetHandler(handler) 55 56 err = buffer.Process(context.Background(), entry.New()) 57 require.NoError(t, err) 58 59 // Tell it to fail as soon as we receive logs 60 <-handler.received 61 handler.fail <- true 62 63 // The next time receive, don't fail, and ensure that we get a success 64 <-handler.received 65 handler.fail <- false 66 <-handler.success 67 }) 68 69 t.Run("ContextCancelled", func(t *testing.T) { 70 cfg := NewConfig() 71 cfg.DelayThreshold = operator.Duration{Duration: 10 * time.Millisecond} 72 buffer, err := cfg.Build() 73 require.NoError(t, err) 74 handler := newMockHandler(t) 75 buffer.SetHandler(handler) 76 77 ctx, cancel := context.WithCancel(context.Background()) 78 err = buffer.Process(ctx, entry.New()) 79 require.NoError(t, err) 80 81 // Fail once, but cancel the context so we don't retry 82 <-handler.received 83 cancel() 84 handler.fail <- true 85 86 // We shouldn't get any more receives or successes 87 select { 88 case <-handler.received: 89 require.FailNow(t, "Received unexpected entries") 90 case <-handler.success: 91 require.FailNow(t, "Received unexpected success") 92 case <-time.After(200 * time.Millisecond): 93 } 94 }) 95 96 t.Run("ExceededLimit", func(t *testing.T) { 97 cfg := NewConfig() 98 cfg.DelayThreshold = operator.Duration{Duration: 10 * time.Millisecond} 99 cfg.Retry.MaxElapsedTime = operator.Duration{Duration: time.Nanosecond} 100 buffer, err := cfg.Build() 101 require.NoError(t, err) 102 handler := newMockHandler(t) 103 buffer.SetHandler(handler) 104 105 err = buffer.Process(context.Background(), entry.New()) 106 require.NoError(t, err) 107 108 // Fail once, which should exceed our 109 // unreasonably low max elapsed time 110 <-handler.received 111 handler.fail <- true 112 113 // We shouldn't get any more receives or successes 114 select { 115 case <-handler.received: 116 require.FailNow(t, "Received unexpected entries") 117 case <-handler.success: 118 require.FailNow(t, "Received unexpected success") 119 case <-time.After(200 * time.Millisecond): 120 } 121 }) 122 } 123 124 func TestMemoryBufferFlush(t *testing.T) { 125 t.Run("Simple", func(t *testing.T) { 126 cfg := NewConfig() 127 cfg.DelayThreshold = operator.Duration{Duration: 10 * time.Hour} 128 buffer, err := cfg.Build() 129 require.NoError(t, err) 130 handler := newMockHandler(t) 131 buffer.SetHandler(handler) 132 133 err = buffer.Process(context.Background(), entry.New()) 134 require.NoError(t, err) 135 136 // We shouldn't have any logs to handle for at 137 // least 10 hours 138 select { 139 case <-handler.received: 140 require.FailNow(t, "Received entry unexpectedly early") 141 case <-time.After(50 * time.Millisecond): 142 } 143 144 flushed := make(chan struct{}) 145 go func() { 146 defer close(flushed) 147 err := buffer.Flush(context.Background()) 148 require.NoError(t, err) 149 }() 150 151 // After flushed is called, we should receive a log 152 <-handler.received 153 handler.fail <- false 154 <-handler.success 155 }) 156 157 t.Run("ContextCancelled", func(t *testing.T) { 158 cfg := NewConfig() 159 cfg.DelayThreshold = operator.Duration{Duration: 10 * time.Hour} 160 buffer, err := cfg.Build() 161 require.NoError(t, err) 162 handler := newMockHandler(t) 163 buffer.SetHandler(handler) 164 165 err = buffer.Process(context.Background(), entry.New()) 166 require.NoError(t, err) 167 168 // We shouldn't have any logs to handle for at 169 // least 10 hours 170 select { 171 case <-handler.received: 172 require.FailNow(t, "Received entry unexpectedly early") 173 case <-time.After(50 * time.Millisecond): 174 } 175 176 // Start the flush 177 ctx, cancel := context.WithCancel(context.Background()) 178 flushed := make(chan struct{}) 179 go func() { 180 defer close(flushed) 181 err := buffer.Flush(ctx) 182 require.Error(t, err) 183 }() 184 185 // Cancel the context and wait for flush to finish 186 cancel() 187 select { 188 case <-flushed: 189 case <-time.After(100 * time.Millisecond): 190 require.FailNow(t, "Failed to flush in reasonable amount of time") 191 } 192 193 // After flushed is called, we should receive a log still, since we 194 // timed out and ignored cleanup 195 <-handler.received 196 handler.fail <- false 197 <-handler.success 198 }) 199 200 }