Bound ordinary runs with attributable operation timeouts
This commit is contained in:
36
internal/usecase/operation_scope.go
Normal file
36
internal/usecase/operation_scope.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OperationScope owns an optional deadline and its invocation-specific cause.
|
||||
type OperationScope struct {
|
||||
Context context.Context
|
||||
cancel context.CancelFunc
|
||||
cause error
|
||||
}
|
||||
|
||||
// NewOperationScope creates a child only when the selected duration is positive.
|
||||
func NewOperationScope(parent context.Context, duration time.Duration) *OperationScope {
|
||||
scope := &OperationScope{Context: parent, cancel: func() {}}
|
||||
if duration > 0 {
|
||||
scope.cause = errors.New("operation deadline")
|
||||
scope.Context, scope.cancel = context.WithTimeoutCause(parent, duration, scope.cause)
|
||||
}
|
||||
return scope
|
||||
}
|
||||
|
||||
// Finalize preserves the failure and marks it only when this scope expired.
|
||||
func (s *OperationScope) Finalize(err error) error {
|
||||
if err != nil && s.cause != nil && s.Context.Err() == context.DeadlineExceeded &&
|
||||
errors.Is(err, s.Context.Err()) && context.Cause(s.Context) == s.cause {
|
||||
return &OperationError{Category: ErrOperationTimeout, Cause: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Close releases the timer and associated child context.
|
||||
func (s *OperationScope) Close() { s.cancel() }
|
||||
Reference in New Issue
Block a user