github.com/Jeffail/benthos/v3@v3.65.0/lib/buffer/none.go (about)

     1  package buffer
     2  
     3  import (
     4  	"sync/atomic"
     5  	"time"
     6  
     7  	"github.com/Jeffail/benthos/v3/internal/docs"
     8  	"github.com/Jeffail/benthos/v3/lib/log"
     9  	"github.com/Jeffail/benthos/v3/lib/metrics"
    10  	"github.com/Jeffail/benthos/v3/lib/types"
    11  )
    12  
    13  //------------------------------------------------------------------------------
    14  
    15  func init() {
    16  	Constructors[TypeNone] = TypeSpec{
    17  		constructor: NewEmpty,
    18  		Summary: `
    19  Do not buffer messages. This is the default and most resilient configuration.`,
    20  		Description: `
    21  Selecting no buffer means the output layer is directly coupled with the input
    22  layer. This is the safest and lowest latency option since acknowledgements from
    23  at-least-once protocols can be propagated all the way from the output protocol
    24  to the input protocol.
    25  
    26  If the output layer is hit with back pressure it will propagate all the way to
    27  the input layer, and further up the data stream. If you need to relieve your
    28  pipeline of this back pressure consider using a more robust buffering solution
    29  such as Kafka before resorting to alternatives.`,
    30  		config: docs.FieldComponent().HasType(docs.FieldTypeObject),
    31  	}
    32  }
    33  
    34  //------------------------------------------------------------------------------
    35  
    36  // Empty is an empty buffer, simply forwards messages on directly.
    37  type Empty struct {
    38  	running int32
    39  
    40  	messagesOut chan types.Transaction
    41  	messagesIn  <-chan types.Transaction
    42  
    43  	closeChan chan struct{}
    44  	closed    chan struct{}
    45  }
    46  
    47  // NewEmpty creates a new buffer interface but doesn't buffer messages.
    48  func NewEmpty(config Config, mgr types.Manager, log log.Modular, stats metrics.Type) (Type, error) {
    49  	e := &Empty{
    50  		running:     1,
    51  		messagesOut: make(chan types.Transaction),
    52  		closeChan:   make(chan struct{}),
    53  		closed:      make(chan struct{}),
    54  	}
    55  	return e, nil
    56  }
    57  
    58  //------------------------------------------------------------------------------
    59  
    60  // loop is an internal loop of the empty buffer.
    61  func (e *Empty) loop() {
    62  	defer func() {
    63  		atomic.StoreInt32(&e.running, 0)
    64  
    65  		close(e.messagesOut)
    66  		close(e.closed)
    67  	}()
    68  
    69  	var open bool
    70  	for atomic.LoadInt32(&e.running) == 1 {
    71  		var inT types.Transaction
    72  		select {
    73  		case inT, open = <-e.messagesIn:
    74  			if !open {
    75  				return
    76  			}
    77  		case <-e.closeChan:
    78  			return
    79  		}
    80  		select {
    81  		case e.messagesOut <- inT:
    82  		case <-e.closeChan:
    83  			return
    84  		}
    85  	}
    86  }
    87  
    88  //------------------------------------------------------------------------------
    89  
    90  // Consume assigns a messages channel for the output to read.
    91  func (e *Empty) Consume(msgs <-chan types.Transaction) error {
    92  	if e.messagesIn != nil {
    93  		return types.ErrAlreadyStarted
    94  	}
    95  	e.messagesIn = msgs
    96  	go e.loop()
    97  	return nil
    98  }
    99  
   100  // TransactionChan returns the channel used for consuming messages from this
   101  // input.
   102  func (e *Empty) TransactionChan() <-chan types.Transaction {
   103  	return e.messagesOut
   104  }
   105  
   106  // ErrorsChan returns the errors channel.
   107  func (e *Empty) ErrorsChan() <-chan []error {
   108  	return nil
   109  }
   110  
   111  // StopConsuming instructs the buffer to no longer consume data.
   112  func (e *Empty) StopConsuming() {
   113  	e.CloseAsync()
   114  }
   115  
   116  // CloseAsync shuts down the StackBuffer output and stops processing messages.
   117  func (e *Empty) CloseAsync() {
   118  	if atomic.CompareAndSwapInt32(&e.running, 1, 0) {
   119  		close(e.closeChan)
   120  	}
   121  }
   122  
   123  // WaitForClose blocks until the StackBuffer output has closed down.
   124  func (e *Empty) WaitForClose(timeout time.Duration) error {
   125  	select {
   126  	case <-e.closed:
   127  	case <-time.After(timeout):
   128  		return types.ErrTimeout
   129  	}
   130  	return nil
   131  }
   132  
   133  //------------------------------------------------------------------------------