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  }