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 //------------------------------------------------------------------------------