github.com/Jeffail/benthos/v3@v3.65.0/internal/transaction/tracked.go (about) 1 package transaction 2 3 import ( 4 "context" 5 "errors" 6 7 "github.com/Jeffail/benthos/v3/internal/batch" 8 imessage "github.com/Jeffail/benthos/v3/internal/message" 9 "github.com/Jeffail/benthos/v3/lib/response" 10 "github.com/Jeffail/benthos/v3/lib/types" 11 ) 12 13 // Tracked is a transaction type that adds identifying tags to messages such 14 // that an error returned resulting from multiple transaction messages can be 15 // reduced. 16 type Tracked struct { 17 msg types.Message 18 group *imessage.SortGroup 19 resChan chan<- types.Response 20 } 21 22 // NewTracked creates a transaction from a message batch and a response channel. 23 // The message is tagged with an identifier for the transaction, and if an error 24 // is returned from a downstream component that merged messages from other 25 // transactions the tag can be used in order to determine whether the message 26 // owned by this transaction succeeded. 27 func NewTracked(msg types.Message, resChan chan<- types.Response) *Tracked { 28 group, trackedMsg := imessage.NewSortGroup(msg) 29 return &Tracked{ 30 msg: trackedMsg, 31 resChan: resChan, 32 group: group, 33 } 34 } 35 36 // Message returns the message owned by this transaction. 37 func (t *Tracked) Message() types.Message { 38 return t.msg 39 } 40 41 // ResponseChan returns the response channel owned by this transaction. 42 func (t *Tracked) ResponseChan() chan<- types.Response { 43 return t.resChan 44 } 45 46 func (t *Tracked) getResFromGroup(walkable batch.WalkableError) types.Response { 47 remainingIndexes := make(map[int]struct{}, t.msg.Len()) 48 for i := 0; i < t.msg.Len(); i++ { 49 remainingIndexes[i] = struct{}{} 50 } 51 52 var res types.Response 53 walkable.WalkParts(func(_ int, p types.Part, err error) bool { 54 if index := t.group.GetIndex(p); index >= 0 { 55 if err != nil { 56 res = response.NewError(err) 57 return false 58 } 59 delete(remainingIndexes, index) 60 if len(remainingIndexes) == 0 { 61 return false 62 } 63 } 64 return true 65 }) 66 if res != nil { 67 return res 68 } 69 70 if len(remainingIndexes) > 0 { 71 return response.NewError(errors.Unwrap(walkable)) 72 } 73 return response.NewAck() 74 } 75 76 func (t *Tracked) resFromError(err error) types.Response { 77 var res types.Response = response.NewAck() 78 if err != nil { 79 if walkable, ok := err.(batch.WalkableError); ok { 80 res = t.getResFromGroup(walkable) 81 } else { 82 res = response.NewError(err) 83 } 84 } 85 return res 86 } 87 88 // Ack provides a response to the upstream service from an error. 89 func (t *Tracked) Ack(ctx context.Context, err error) error { 90 select { 91 case t.resChan <- t.resFromError(err): 92 case <-ctx.Done(): 93 return context.Canceled 94 } 95 return nil 96 } 97 98 //------------------------------------------------------------------------------