package common_utils import ( "context" "errors" "fmt" "sync" "time" ) func Retry( ctx context.Context, retryTimes int, waitBeforeRetry time.Duration, exec Task, ) (success bool, errs []error) { var err error errs = []error{} for i := 0; i < retryTimes; i++ { if err = exec(ctx); err == nil { return true, errs } errs = append(errs, err) } return false, errs } func ExecTask(ctx context.Context, tasks Task) error { errChan := make(chan error) go func() { errChan <- tasks(ctx) }() select { case err := <-errChan: return err case <-ctx.Done(): return ctx.Err() } } // ExecTasks Runs each task in a separate goroutine // To set no task timeout -- set taskTimeout to 0 func ExecTasks(ctx context.Context, taskTimeout time.Duration, tasks []Task) error { wg := &sync.WaitGroup{} errChan := make(chan error) wgChan := make(chan struct{}) wg.Add(len(tasks)) for i, task := range tasks { go func() { var cancel context.CancelFunc taskCtx := context.Background() if taskTimeout != 0 { taskCtx, cancel = context.WithTimeout(taskCtx, taskTimeout) } if err := task(taskCtx); err != nil { errChan <- errors.Join(fmt.Errorf("error running task %d: ", i), err) } wg.Done() cancel() }() } go func() { wg.Wait() close(wgChan) }() select { case <-ctx.Done(): return ctx.Err() case <-wgChan: return nil case err := <-errChan: return err } } func Ternar[T any](condition bool, ifTrue T, ifFalse T) T { if condition { return ifTrue } return ifFalse } func Must(err error) { if err != nil { panic(err.Error()) } }