GoSentry is a lightweight, composable policy-based execution framework for Go. It provides a clean middleware-style pattern for wrapping handlers with various execution policies like retry, circuit breaking, timeouts, and more.
GoSentry follows a simple Handler → Policy → Execute pattern:
- Handler: A function that performs the actual work
- Policy: A middleware function that wraps and enhances handlers
- Execute: Composes multiple policies and executes the handler
go get gosentrypackage main
import (
"context"
"gosentry"
"gosentry/policies"
"time"
)
func main() {
// Configure retry policy
retryOptions := policies.RetryOptions{
MaxAttempts: 3,
InitialDelay: 1 * time.Second,
MaxDelay: 10 * time.Second,
Backoff: policies.BackoffExponential,
Jitter: true,
}
retryPolicy := policies.Retry(retryOptions)
// Define your handler
handler := func(ctx context.Context) (any, error) {
// Your business logic here
return "result", nil
}
// Execute with policies
result, err := gosentry.Execute(context.Background(), handler, retryPolicy)
if err != nil {
// Handle error
}
}The retry policy automatically retries failed operations with configurable backoff strategies.
Features:
- Configurable max attempts
- Multiple backoff strategies (fixed, linear, exponential)
- Optional jitter to prevent thundering herd
- Context-aware cancellation
Example:
retryOptions := policies.RetryOptions{
MaxAttempts: 3,
InitialDelay: 100 * time.Millisecond,
MaxDelay: 5 * time.Second,
Backoff: policies.BackoffExponential,
Jitter: true,
}
retryPolicy := policies.Retry(retryOptions)Backoff Strategies:
BackoffFixed: Constant delay between retriesBackoffLinear: Linear increase in delayBackoffExponential: Exponential backoff (default)
The circuit breaker policy prevents cascading failures by opening after a threshold of consecutive failures, rejecting calls for a cooldown period, then allowing a half-open trial call to determine recovery.
Features:
- Configurable failure threshold (consecutive)
- Open timeout cooldown
- Half-open trial (single in-flight call)
- Configurable success threshold to close
- Optional
ShouldTripfilter for which errors count
Example:
cb := policies.CircuitBreaker(policies.CircuitBreakerOptions{
FailureThreshold: 3,
SuccessThreshold: 1,
OpenTimeout: 5 * time.Second,
ShouldTrip: func(err error) bool {
return err != nil
},
})
result, err := gosentry.Execute(ctx, handler, cb)The timeout policy enforces a maximum execution time for handlers by applying a context.WithTimeout and returning context.DeadlineExceeded when the deadline is reached.
Example:
timeoutPolicy := policies.Timeout(policies.TimeoutOptions{
Duration: 250 * time.Millisecond,
})
result, err := gosentry.Execute(ctx, handler, timeoutPolicy)Policies are applied in reverse order (last policy wraps first):
result, err := gosentry.Execute(
ctx,
handler,
timeoutPolicy, // Applied first (outermost)
retryPolicy, // Applied second
circuitBreakerPolicy, // Applied last (innermost)
)The following policies are implemented or planned:
- Retry - Automatically retry failed operations with configurable backoff strategies
- Circuit Breaker - Prevent cascading failures by opening circuit after threshold failures
- Timeout - Enforce maximum execution time for handlers
- Rate Limiting - Control the rate of execution (token bucket, sliding window)
- Bulkhead - Isolate execution contexts to prevent resource exhaustion
- Fallback - Provide default values or alternative handlers on failure
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details.
