github.com/Jeffail/benthos/v3@v3.65.0/lib/processor/util.go (about)

     1  package processor
     2  
     3  import (
     4  	"github.com/Jeffail/benthos/v3/internal/tracing"
     5  	"github.com/Jeffail/benthos/v3/lib/message"
     6  	"github.com/Jeffail/benthos/v3/lib/response"
     7  	"github.com/Jeffail/benthos/v3/lib/types"
     8  	"github.com/opentracing/opentracing-go"
     9  	olog "github.com/opentracing/opentracing-go/log"
    10  )
    11  
    12  //------------------------------------------------------------------------------
    13  
    14  // ExecuteAll attempts to execute a slice of processors to a message. Returns
    15  // N resulting messages or a response. The response may indicate either a NoAck
    16  // in the event of the message being buffered or an unrecoverable error.
    17  func ExecuteAll(procs []types.Processor, msgs ...types.Message) ([]types.Message, types.Response) {
    18  	resultMsgs := make([]types.Message, len(msgs))
    19  	copy(resultMsgs, msgs)
    20  
    21  	var resultRes types.Response
    22  	for i := 0; len(resultMsgs) > 0 && i < len(procs); i++ {
    23  		var nextResultMsgs []types.Message
    24  		for _, m := range resultMsgs {
    25  			var rMsgs []types.Message
    26  			if rMsgs, resultRes = procs[i].ProcessMessage(m); resultRes != nil && resultRes.Error() != nil {
    27  				// We immediately return if a processor hits an unrecoverable
    28  				// error on a message.
    29  				return nil, resultRes
    30  			}
    31  			nextResultMsgs = append(nextResultMsgs, rMsgs...)
    32  		}
    33  		resultMsgs = nextResultMsgs
    34  	}
    35  
    36  	if len(resultMsgs) == 0 {
    37  		if resultRes == nil {
    38  			resultRes = response.NewUnack()
    39  		}
    40  		return nil, resultRes
    41  	}
    42  	return resultMsgs, nil
    43  }
    44  
    45  // ExecuteTryAll attempts to execute a slice of processors to messages, if a
    46  // message has failed a processing step it is prevented from being sent to
    47  // subsequent processors. Returns N resulting messages or a response. The
    48  // response may indicate either a NoAck in the event of the message being
    49  // buffered or an unrecoverable error.
    50  func ExecuteTryAll(procs []types.Processor, msgs ...types.Message) ([]types.Message, types.Response) {
    51  	resultMsgs := make([]types.Message, len(msgs))
    52  	copy(resultMsgs, msgs)
    53  
    54  	var resultRes types.Response
    55  	for i := 0; len(resultMsgs) > 0 && i < len(procs); i++ {
    56  		var nextResultMsgs []types.Message
    57  		for _, m := range resultMsgs {
    58  			// Skip messages that failed a prior stage.
    59  			if HasFailed(m.Get(0)) {
    60  				nextResultMsgs = append(nextResultMsgs, m)
    61  				continue
    62  			}
    63  			var rMsgs []types.Message
    64  			if rMsgs, resultRes = procs[i].ProcessMessage(m); resultRes != nil && resultRes.Error() != nil {
    65  				// We immediately return if a processor hits an unrecoverable
    66  				// error on a message.
    67  				return nil, resultRes
    68  			}
    69  			nextResultMsgs = append(nextResultMsgs, rMsgs...)
    70  		}
    71  		resultMsgs = nextResultMsgs
    72  	}
    73  
    74  	if len(resultMsgs) == 0 {
    75  		if resultRes == nil {
    76  			resultRes = response.NewUnack()
    77  		}
    78  		return nil, resultRes
    79  	}
    80  	return resultMsgs, nil
    81  }
    82  
    83  type catchMessage struct {
    84  	batches []types.Message
    85  	caught  bool
    86  }
    87  
    88  // ExecuteCatchAll attempts to execute a slice of processors to only messages
    89  // that have failed a processing step. Returns N resulting messages or a
    90  // response.
    91  func ExecuteCatchAll(procs []types.Processor, msgs ...types.Message) ([]types.Message, types.Response) {
    92  	// Preserves the original order of messages before entering the catch block.
    93  	// Only processors that have failed a previous stage are "caught", and will
    94  	// remain caught until all catch processors are executed.
    95  	catchBatches := make([]catchMessage, len(msgs))
    96  	for i, m := range msgs {
    97  		catchBatches[i] = catchMessage{
    98  			batches: []types.Message{m},
    99  			caught:  HasFailed(m.Get(0)),
   100  		}
   101  	}
   102  
   103  	var resultRes types.Response
   104  	for i := 0; i < len(procs); i++ {
   105  		for j := 0; j < len(catchBatches); j++ {
   106  			if !catchBatches[j].caught || len(catchBatches[j].batches) == 0 {
   107  				continue
   108  			}
   109  
   110  			var nextResultBatches []types.Message
   111  			for _, m := range catchBatches[j].batches {
   112  				var rMsgs []types.Message
   113  				if rMsgs, resultRes = procs[i].ProcessMessage(m); resultRes != nil && resultRes.Error() != nil {
   114  					// We immediately return if a processor hits an unrecoverable
   115  					// error on a message.
   116  					return nil, resultRes
   117  				}
   118  				nextResultBatches = append(nextResultBatches, rMsgs...)
   119  			}
   120  			catchBatches[j].batches = nextResultBatches
   121  		}
   122  	}
   123  
   124  	var resultBatches []types.Message
   125  	for _, b := range catchBatches {
   126  		resultBatches = append(resultBatches, b.batches...)
   127  	}
   128  
   129  	if len(resultBatches) == 0 {
   130  		if resultRes == nil {
   131  			resultRes = response.NewUnack()
   132  		}
   133  		return nil, resultRes
   134  	}
   135  	return resultBatches, nil
   136  }
   137  
   138  //------------------------------------------------------------------------------
   139  
   140  // FailFlagKey is a metadata key used for flagging processor errors in Benthos.
   141  // If a message part has any non-empty value for this metadata key then it will
   142  // be interpretted as having failed a processor step somewhere in the pipeline.
   143  var FailFlagKey = types.FailFlagKey
   144  
   145  // FlagFail marks a message part as having failed at a processing step.
   146  func FlagFail(part types.Part) {
   147  	part.Metadata().Set(FailFlagKey, "true")
   148  }
   149  
   150  // FlagErr marks a message part as having failed at a processing step with an
   151  // error message. If the error is nil the message part remains unchanged.
   152  func FlagErr(part types.Part, err error) {
   153  	if err != nil {
   154  		part.Metadata().Set(FailFlagKey, err.Error())
   155  	}
   156  }
   157  
   158  // GetFail returns an error string for a message part if it has failed, or an
   159  // empty string if not.
   160  func GetFail(part types.Part) string {
   161  	return part.Metadata().Get(FailFlagKey)
   162  }
   163  
   164  // HasFailed checks whether a message part has failed a processing step.
   165  func HasFailed(part types.Part) bool {
   166  	return len(part.Metadata().Get(FailFlagKey)) > 0
   167  }
   168  
   169  // ClearFail removes any existing failure flags from a message part.
   170  func ClearFail(part types.Part) {
   171  	part.Metadata().Delete(FailFlagKey)
   172  }
   173  
   174  //------------------------------------------------------------------------------
   175  
   176  func iterateParts(
   177  	parts []int, msg types.Message,
   178  	iter func(int, types.Part) error,
   179  ) error {
   180  	exec := func(i int) error {
   181  		return iter(i, msg.Get(i))
   182  	}
   183  	if len(parts) == 0 {
   184  		for i := 0; i < msg.Len(); i++ {
   185  			if err := exec(i); err != nil {
   186  				return err
   187  			}
   188  		}
   189  	} else {
   190  		for _, i := range parts {
   191  			if err := exec(i); err != nil {
   192  				return err
   193  			}
   194  		}
   195  	}
   196  	return nil
   197  }
   198  
   199  // IteratePartsWithSpanV2 iterates the parts of a message according to a slice
   200  // of indexes (if empty all parts are iterated) and calls a func for each part
   201  // along with a tracing span for that part. If an error is returned the part is
   202  // flagged as failed and the span has the error logged.
   203  func IteratePartsWithSpanV2(
   204  	operationName string, parts []int, msg types.Message,
   205  	iter func(int, *tracing.Span, types.Part) error,
   206  ) {
   207  	exec := func(i int) {
   208  		part := msg.Get(i)
   209  		span := tracing.CreateChildSpan(operationName, part)
   210  
   211  		if err := iter(i, span, part); err != nil {
   212  			FlagErr(part, err)
   213  			span.SetTag("error", true)
   214  			span.LogKV(
   215  				"event", "error",
   216  				"type", err.Error(),
   217  			)
   218  		}
   219  		span.Finish()
   220  	}
   221  	if len(parts) == 0 {
   222  		for i := 0; i < msg.Len(); i++ {
   223  			exec(i)
   224  		}
   225  	} else {
   226  		for _, i := range parts {
   227  			exec(i)
   228  		}
   229  	}
   230  }
   231  
   232  // IteratePartsWithSpan iterates the parts of a message according to a slice of
   233  // indexes (if empty all parts are iterated) and calls a func for each part
   234  // along with a tracing span for that part. If an error is returned the part is
   235  // flagged as failed and the span has the error logged.
   236  //
   237  // Deprecated: use IteratePartsWithSpanV2 instead.
   238  func IteratePartsWithSpan(
   239  	operationName string, parts []int, msg types.Message,
   240  	iter func(int, opentracing.Span, types.Part) error,
   241  ) {
   242  	exec := func(i int) {
   243  		part := msg.Get(i)
   244  		span := opentracing.SpanFromContext(message.GetContext(part))
   245  		if span == nil {
   246  			span = opentracing.StartSpan(operationName)
   247  		} else {
   248  			span = opentracing.StartSpan(
   249  				operationName,
   250  				opentracing.ChildOf(span.Context()),
   251  			)
   252  		}
   253  		if err := iter(i, span, part); err != nil {
   254  			FlagErr(part, err)
   255  			span.SetTag("error", true)
   256  			span.LogFields(
   257  				olog.String("event", "error"),
   258  				olog.String("type", err.Error()),
   259  			)
   260  		}
   261  		span.Finish()
   262  	}
   263  	if len(parts) == 0 {
   264  		for i := 0; i < msg.Len(); i++ {
   265  			exec(i)
   266  		}
   267  	} else {
   268  		for _, i := range parts {
   269  			exec(i)
   270  		}
   271  	}
   272  }
   273  
   274  // Iterate the parts of a message, mutate them as required, and return either a
   275  // boolean or an error. If the error is nil and the boolean is false then the
   276  // message part is removed.
   277  func iteratePartsFilterableWithSpan(
   278  	operationName string, parts []int, msg types.Message,
   279  	iter func(int, *tracing.Span, types.Part) (bool, error),
   280  ) {
   281  	newParts := make([]types.Part, 0, msg.Len())
   282  	exec := func(i int) bool {
   283  		part := msg.Get(i)
   284  		span := tracing.CreateChildSpan(operationName, part)
   285  
   286  		var keep bool
   287  		var err error
   288  		if keep, err = iter(i, span, part); err != nil {
   289  			FlagErr(part, err)
   290  			span.SetTag("error", true)
   291  			span.LogKV(
   292  				"event", "error",
   293  				"type", err.Error(),
   294  			)
   295  			keep = true
   296  		}
   297  		span.Finish()
   298  		return keep
   299  	}
   300  
   301  	if len(parts) == 0 {
   302  		for i := 0; i < msg.Len(); i++ {
   303  			if exec(i) {
   304  				newParts = append(newParts, msg.Get(i))
   305  			}
   306  		}
   307  	} else {
   308  		for _, i := range parts {
   309  			if exec(i) {
   310  				newParts = append(newParts, msg.Get(i))
   311  			}
   312  		}
   313  	}
   314  
   315  	msg.SetAll(newParts)
   316  }
   317  
   318  //------------------------------------------------------------------------------