github.com/Jeffail/benthos/v3@v3.65.0/lib/input/reader/sync_batcher_test.go (about) 1 package reader 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "testing" 8 "time" 9 10 "github.com/Jeffail/benthos/v3/lib/log" 11 "github.com/Jeffail/benthos/v3/lib/message" 12 "github.com/Jeffail/benthos/v3/lib/message/batch" 13 "github.com/Jeffail/benthos/v3/lib/metrics" 14 "github.com/Jeffail/benthos/v3/lib/types" 15 ) 16 17 //------------------------------------------------------------------------------ 18 19 type mockSyncReader struct { 20 msgsToSnd []types.Message 21 ackRcvd error 22 23 connChan chan error 24 readChan chan error 25 ackChan chan error 26 closeAsyncChan chan struct{} 27 waitForCloseChan chan error 28 } 29 30 func newMockSyncReader() *mockSyncReader { 31 return &mockSyncReader{ 32 connChan: make(chan error), 33 readChan: make(chan error), 34 ackChan: make(chan error), 35 closeAsyncChan: make(chan struct{}), 36 waitForCloseChan: make(chan error), 37 } 38 } 39 40 func (r *mockSyncReader) ConnectWithContext(ctx context.Context) error { 41 cerr, open := <-r.connChan 42 if !open { 43 return types.ErrNotConnected 44 } 45 return cerr 46 } 47 48 func (r *mockSyncReader) ReadNextWithContext(ctx context.Context) (types.Message, error) { 49 select { 50 case <-ctx.Done(): 51 return nil, types.ErrTimeout 52 case err, open := <-r.readChan: 53 if !open { 54 return nil, types.ErrNotConnected 55 } 56 if err != nil { 57 return nil, err 58 } 59 } 60 61 var nextMsg types.Message = message.New(nil) 62 if len(r.msgsToSnd) > 0 { 63 nextMsg = r.msgsToSnd[0] 64 r.msgsToSnd = r.msgsToSnd[1:] 65 } 66 67 return nextMsg.DeepCopy(), nil 68 } 69 70 func (r *mockSyncReader) AcknowledgeWithContext(ctx context.Context, err error) error { 71 r.ackRcvd = err 72 return <-r.ackChan 73 } 74 75 func (r *mockSyncReader) CloseAsync() { 76 <-r.closeAsyncChan 77 } 78 79 func (r *mockSyncReader) WaitForClose(time.Duration) error { 80 return <-r.waitForCloseChan 81 } 82 83 //------------------------------------------------------------------------------ 84 85 func TestSyncBatcherHappy(t *testing.T) { 86 ctx, done := context.WithTimeout(context.Background(), time.Second*10) 87 defer done() 88 89 testMsgs := []string{} 90 for i := 0; i < 10; i++ { 91 testMsgs = append(testMsgs, fmt.Sprintf("test %v", i)) 92 } 93 rdr := newMockSyncReader() 94 for _, str := range testMsgs { 95 rdr.msgsToSnd = append(rdr.msgsToSnd, message.New([][]byte{[]byte(str)})) 96 } 97 98 conf := batch.NewPolicyConfig() 99 conf.Count = 5 100 batcher, err := NewSyncBatcher(conf, rdr, nil, log.Noop(), metrics.Noop()) 101 if err != nil { 102 t.Fatal(err) 103 } 104 defer func() { 105 batcher.CloseAsync() 106 deadline, _ := ctx.Deadline() 107 if err = batcher.WaitForClose(time.Until(deadline)); err != nil { 108 t.Error(err) 109 } 110 }() 111 112 lastErr := errors.New("test error") 113 go func() { 114 rdr.connChan <- nil 115 for i := 0; i < 5; i++ { 116 rdr.readChan <- nil 117 } 118 rdr.ackChan <- nil 119 for i := 0; i < 5; i++ { 120 rdr.readChan <- nil 121 } 122 rdr.ackChan <- lastErr 123 rdr.closeAsyncChan <- struct{}{} 124 rdr.waitForCloseChan <- nil 125 }() 126 127 if err = batcher.ConnectWithContext(ctx); err != nil { 128 t.Fatal(err) 129 } 130 131 msg, err := batcher.ReadNextWithContext(ctx) 132 if err != nil { 133 t.Fatal(err) 134 } 135 if msg.Len() != 5 { 136 t.Errorf("Wrong batch count: %v", msg.Len()) 137 } 138 msg.Iter(func(i int, part types.Part) error { 139 if exp, act := fmt.Sprintf("test %v", i), string(part.Get()); exp != act { 140 t.Errorf("Wrong message contents: %v != %v", act, exp) 141 } 142 return nil 143 }) 144 if err = batcher.AcknowledgeWithContext(ctx, nil); err != nil { 145 t.Error(err) 146 } 147 148 if msg, err = batcher.ReadNextWithContext(ctx); err != nil { 149 t.Fatal(err) 150 } 151 if msg.Len() != 5 { 152 t.Errorf("Wrong batch count: %v", msg.Len()) 153 } 154 msg.Iter(func(i int, part types.Part) error { 155 if exp, act := fmt.Sprintf("test %v", i+5), string(part.Get()); exp != act { 156 t.Errorf("Wrong message contents: %v != %v", act, exp) 157 } 158 return nil 159 }) 160 if err = batcher.AcknowledgeWithContext(ctx, nil); err != lastErr { 161 t.Errorf("Expected '%v', received: %v", lastErr, err) 162 } 163 } 164 165 func TestSyncBatcherSadThenHappy(t *testing.T) { 166 ctx, done := context.WithTimeout(context.Background(), time.Second*10) 167 defer done() 168 169 testMsgs := []string{} 170 for i := 0; i < 10; i++ { 171 testMsgs = append(testMsgs, fmt.Sprintf("test %v", i)) 172 } 173 rdr := newMockSyncReader() 174 for _, str := range testMsgs { 175 rdr.msgsToSnd = append(rdr.msgsToSnd, message.New([][]byte{[]byte(str)})) 176 } 177 178 conf := batch.NewPolicyConfig() 179 conf.Count = 5 180 batcher, err := NewSyncBatcher(conf, rdr, nil, log.Noop(), metrics.Noop()) 181 if err != nil { 182 t.Fatal(err) 183 } 184 defer func() { 185 batcher.CloseAsync() 186 deadline, _ := ctx.Deadline() 187 if err = batcher.WaitForClose(time.Until(deadline)); err != nil { 188 t.Error(err) 189 } 190 }() 191 192 firstReadErr := errors.New("reading failed 1") 193 secondReadErr := errors.New("reading failed 2") 194 go func() { 195 rdr.connChan <- nil 196 rdr.readChan <- firstReadErr 197 for i := 0; i < 5; i++ { 198 rdr.readChan <- nil 199 } 200 rdr.ackChan <- nil 201 for i := 0; i < 2; i++ { 202 rdr.readChan <- nil 203 } 204 rdr.readChan <- secondReadErr 205 for i := 0; i < 3; i++ { 206 rdr.readChan <- nil 207 } 208 rdr.ackChan <- nil 209 rdr.closeAsyncChan <- struct{}{} 210 rdr.waitForCloseChan <- nil 211 }() 212 213 if err = batcher.ConnectWithContext(ctx); err != nil { 214 t.Fatal(err) 215 } 216 217 if _, err = batcher.ReadNextWithContext(ctx); err != firstReadErr { 218 t.Fatalf("Expected '%v', received: %v", firstReadErr, err) 219 } 220 221 msg, err := batcher.ReadNextWithContext(ctx) 222 if err != nil { 223 t.Fatal(err) 224 } 225 if msg.Len() != 5 { 226 t.Errorf("Wrong batch count: %v", msg.Len()) 227 } 228 msg.Iter(func(i int, part types.Part) error { 229 if exp, act := fmt.Sprintf("test %v", i), string(part.Get()); exp != act { 230 t.Errorf("Wrong message contents: %v != %v", act, exp) 231 } 232 return nil 233 }) 234 if err = batcher.AcknowledgeWithContext(ctx, nil); err != nil { 235 t.Error(err) 236 } 237 238 if _, err = batcher.ReadNextWithContext(ctx); err != secondReadErr { 239 t.Fatalf("Expected '%v', received: %v", secondReadErr, err) 240 } 241 242 if msg, err = batcher.ReadNextWithContext(ctx); err != nil { 243 t.Fatal(err) 244 } 245 if msg.Len() != 5 { 246 t.Errorf("Wrong batch count: %v", msg.Len()) 247 } 248 msg.Iter(func(i int, part types.Part) error { 249 if exp, act := fmt.Sprintf("test %v", i+5), string(part.Get()); exp != act { 250 t.Errorf("Wrong message contents: %v != %v", act, exp) 251 } 252 return nil 253 }) 254 if err = batcher.AcknowledgeWithContext(ctx, nil); err != nil { 255 t.Error(err) 256 } 257 } 258 259 func TestSyncBatcherTimedBatches(t *testing.T) { 260 ctx, done := context.WithTimeout(context.Background(), time.Second*10) 261 defer done() 262 263 testMsgs := []string{} 264 for i := 0; i < 10; i++ { 265 testMsgs = append(testMsgs, fmt.Sprintf("test %v", i)) 266 } 267 rdr := newMockSyncReader() 268 for _, str := range testMsgs { 269 rdr.msgsToSnd = append(rdr.msgsToSnd, message.New([][]byte{[]byte(str)})) 270 } 271 272 conf := batch.NewPolicyConfig() 273 conf.Count = 8 274 conf.Period = "500ms" 275 batcher, err := NewSyncBatcher(conf, rdr, nil, log.Noop(), metrics.Noop()) 276 if err != nil { 277 t.Fatal(err) 278 } 279 defer func() { 280 batcher.CloseAsync() 281 deadline, _ := ctx.Deadline() 282 if err = batcher.WaitForClose(time.Until(deadline)); err != nil { 283 t.Error(err) 284 } 285 }() 286 287 go func() { 288 rdr.connChan <- nil 289 // Only send two messages through. 290 for i := 0; i < 2; i++ { 291 rdr.readChan <- nil 292 } 293 rdr.readChan <- types.ErrTimeout 294 rdr.ackChan <- nil 295 for i := 0; i < 8; i++ { 296 rdr.readChan <- nil 297 } 298 rdr.ackChan <- nil 299 rdr.closeAsyncChan <- struct{}{} 300 rdr.waitForCloseChan <- nil 301 }() 302 303 if err = batcher.ConnectWithContext(ctx); err != nil { 304 t.Fatal(err) 305 } 306 307 msg, err := batcher.ReadNextWithContext(ctx) 308 if err != nil { 309 t.Fatal(err) 310 } 311 if msg.Len() != 2 { 312 t.Errorf("Wrong batch count: %v", msg.Len()) 313 } 314 msg.Iter(func(i int, part types.Part) error { 315 if exp, act := fmt.Sprintf("test %v", i), string(part.Get()); exp != act { 316 t.Errorf("Wrong message contents: %v != %v", act, exp) 317 } 318 return nil 319 }) 320 if err = batcher.AcknowledgeWithContext(ctx, nil); err != nil { 321 t.Error(err) 322 } 323 324 if msg, err = batcher.ReadNextWithContext(ctx); err != nil { 325 t.Fatal(err) 326 } 327 if msg.Len() != 8 { 328 t.Errorf("Wrong batch count: %v", msg.Len()) 329 } 330 msg.Iter(func(i int, part types.Part) error { 331 if exp, act := fmt.Sprintf("test %v", i+2), string(part.Get()); exp != act { 332 t.Errorf("Wrong message contents: %v != %v", act, exp) 333 } 334 return nil 335 }) 336 if err = batcher.AcknowledgeWithContext(ctx, nil); err != nil { 337 t.Error(err) 338 } 339 }