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  }