-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrun.go
More file actions
49 lines (41 loc) · 1.24 KB
/
run.go
File metadata and controls
49 lines (41 loc) · 1.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package circuit
import (
"context"
"errors"
"fmt"
"sync/atomic"
"time"
)
// Run executes fn with circuit breaker protection, returning a typed result.
// The function is called synchronously — the caller controls concurrency.
// If a timeout is configured, it is applied via context.WithTimeout;
// the runner must respect context cancellation for timeouts to take effect.
// Panics in fn are caught, recorded as failures, and re-panicked.
func Run[T any](b *Breaker, ctx context.Context, fn func(context.Context) (T, error)) (T, error) {
var zero T
if b.tracker == nil {
return zero, ErrNotInitialized
}
if err := b.checkFitness(ctx); err != nil {
return zero, err
}
if b.timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, b.timeout)
defer cancel()
}
start := time.Now()
defer func() {
if r := recover(); r != nil {
b.recordOutcome(fmt.Errorf("panic: %v", r), time.Since(start))
panic(r)
}
}()
result, err := fn(ctx)
b.recordOutcome(err, time.Since(start))
// convert context deadline errors to ErrTimeout for the caller
if err != nil && errors.Is(err, context.DeadlineExceeded) {
return result, ErrTimeout.withContext(b.name, State(atomic.LoadUint32(&b.state)))
}
return result, err
}