added poseidon with aws to k8s changes
This commit is contained in:
66
pkg/util/merge_context.go
Normal file
66
pkg/util/merge_context.go
Normal file
@ -0,0 +1,66 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// mergeContext combines multiple contexts.
|
||||
type mergeContext struct {
|
||||
contexts []context.Context
|
||||
}
|
||||
|
||||
func NewMergeContext(contexts []context.Context) context.Context {
|
||||
return mergeContext{contexts: contexts}
|
||||
}
|
||||
|
||||
// Deadline returns the earliest Deadline of all contexts.
|
||||
func (m mergeContext) Deadline() (deadline time.Time, ok bool) {
|
||||
for _, ctx := range m.contexts {
|
||||
if anotherDeadline, anotherOk := ctx.Deadline(); anotherOk {
|
||||
if ok && anotherDeadline.After(deadline) {
|
||||
continue
|
||||
}
|
||||
deadline = anotherDeadline
|
||||
ok = anotherOk
|
||||
}
|
||||
}
|
||||
return deadline, ok
|
||||
}
|
||||
|
||||
// Done notifies when the first context is done.
|
||||
func (m mergeContext) Done() <-chan struct{} {
|
||||
ch := make(chan struct{})
|
||||
cases := make([]reflect.SelectCase, 0, len(m.contexts))
|
||||
for _, ctx := range m.contexts {
|
||||
cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ctx.Done())})
|
||||
}
|
||||
go func(cases []reflect.SelectCase, ch chan struct{}) {
|
||||
_, _, _ = reflect.Select(cases)
|
||||
close(ch)
|
||||
}(cases, ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
// Err returns the error of any (random) context and nil iff no context has an error.
|
||||
func (m mergeContext) Err() error {
|
||||
for _, ctx := range m.contexts {
|
||||
if ctx.Err() != nil {
|
||||
return fmt.Errorf("mergeContext wrapped: %w", ctx.Err())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value returns the value for the key if any context has it.
|
||||
// If multiple contexts have a value for the key, the result is any (random) of them.
|
||||
func (m mergeContext) Value(key any) any {
|
||||
for _, ctx := range m.contexts {
|
||||
if value := ctx.Value(key); value != nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
82
pkg/util/util.go
Normal file
82
pkg/util/util.go
Normal file
@ -0,0 +1,82 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/openHPI/poseidon/pkg/logging"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logging.GetLogger("util")
|
||||
// MaxConnectionRetriesExponential is the default number of retries. It's exported for testing reasons.
|
||||
MaxConnectionRetriesExponential = 18
|
||||
// InitialWaitingDuration is the default initial duration of waiting after a failed time.
|
||||
InitialWaitingDuration = time.Second
|
||||
ErrRetryContextDone = errors.New("the retry context is done")
|
||||
)
|
||||
|
||||
func retryExponential(ctx context.Context, sleep time.Duration, f func() error) func() error {
|
||||
return func() error {
|
||||
err := f()
|
||||
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ErrRetryContextDone
|
||||
case <-time.After(sleep):
|
||||
sleep *= 2
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func retryConstant(ctx context.Context, sleep time.Duration, f func() error) func() error {
|
||||
return func() error {
|
||||
err := f()
|
||||
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ErrRetryContextDone
|
||||
case <-time.After(sleep):
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func retryAttempts(maxAttempts int, f func() error) (err error) {
|
||||
for i := 0; i < maxAttempts; i++ {
|
||||
err = f()
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if errors.Is(err, ErrRetryContextDone) {
|
||||
return err
|
||||
}
|
||||
log.WithField("count", i).WithError(err).Debug("retrying after error")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// RetryExponentialWithContext executes the passed function with exponentially increasing time starting with one second
|
||||
// up to a default maximum number of attempts as long as the context is not done.
|
||||
func RetryExponentialWithContext(ctx context.Context, f func() error) error {
|
||||
return retryAttempts(MaxConnectionRetriesExponential, retryExponential(ctx, InitialWaitingDuration, f))
|
||||
}
|
||||
|
||||
// RetryExponential executes the passed function with exponentially increasing time starting with one second
|
||||
// up to a default maximum number of attempts.
|
||||
func RetryExponential(f func() error) error {
|
||||
return retryAttempts(MaxConnectionRetriesExponential,
|
||||
retryExponential(context.Background(), InitialWaitingDuration, f))
|
||||
}
|
||||
|
||||
// RetryConstantAttemptsWithContext executes the passed function with a constant retry delay of one second
|
||||
// up to the passed maximum number of attempts as long as the context is not done.
|
||||
func RetryConstantAttemptsWithContext(attempts int, ctx context.Context, f func() error) error {
|
||||
return retryAttempts(attempts, retryConstant(ctx, InitialWaitingDuration, f))
|
||||
}
|
Reference in New Issue
Block a user