go-redis
go-redis copied to clipboard
Support for Combining all Pipeline Error Strings With Command Name
Issue tracker is used for reporting bugs and discussing new features. Please use stackoverflow for supporting issues.
Expected Behavior
It would be nice to have a method for Aggregating and Joining all failed commands in a Pipeline for greater context on each command that failed within a pipeline in a concise easy to read manner.
Iterating through each command and checking the result, adding failed commands to a slice. After which joining each of the strings together and returning as an error.
Current Behavior
Only built-in support for the first failed command.
Possible Solution
A struct that links the Command Name and Command Error. A type which satisfies the error interface that is a slice of the above mentioned struct
A function that takes in a []redis.Cmder or a method on Cmder. This function/method would iterate the slice, checking each command for error. Adding the command name and error to a struct and appending this struct to a slice.
A method on the custom type that satisfies the error interface that would format the command name and string, and join each formatted string in the return.
Context (Environment)
It is useful, especially with longer and/or mixed command pipelines to know all commands that failed. I have found the above mentioned feature to be especially helpful when used in conjunction with errors.Wrapf(). This has saved me quite a bit of time debugging complex systems.
Detailed Description
Providing a quick and easy method for the client to get a pipeline return containing an aggregation of all failed commands. This method should provide the Command Name, Command Number, and the Error message for the command. The aggregation would be most useful as an Error() string method satifying the error interface, allowing for the aggregated message to be returned as 1 error. This would allow the error to be easily wrapped, allowing the client/consumer to easily track all failures.
I do understand this can be accomplished now by iterating the returned []Cmder from the Exec() method, it can however get messy when multiple commands fail in a pipeline, especially if the intent is to wrap the error neatly. Difficult? Absolutely not. QOL fixes are always nice as we all know engineering time is very costly.
Possible Implementation
Extremely rough example code
type PipelineError struct {
Err error
FailedCmd string
}
type PipelineErrors []PipelineError
func (p PipelineErrors) Error() string {
if len(p) == 0 {
return "nil"
}
var errStr []string
for i, e := range p {
if e.Err == nil {
continue
}
errStr = append(errStr, fmt.Sprintf("Command: #%d: %s Error Message: %s", i+1, e.Command, e.Err.Error()))
}
if len(errStr) == 0 {
return "nil"
}
return strings.Join(errStr, ";")
}
func cmdsCombineAllErrors(cmds []Cmder) error {
var errs PipelineErrors
for _, cmd := range cmds {
if cmd.Err() == nil {
continue
}
errs = append(errs, PipelineError{
FailedCmd: cmd.Name(),
Err: cmd.Err(),
})
}
if len(errs) == 0 {
return nil
}
return errs
}
func (c *Pipeline) ExecCombinedErrors(ctx context.Context) ([]Cmder, error) {
if len(c.cmds) == 0 {
return nil, nil
}
if cmds, err := c.Exec(ctx); err != nil {
return cmds, cmdsCombineAllErrors(cmds)
} else {
return cmds, nil
}
}