package recovery import ( "time" "go.temporal.io/sdk/temporal" "go.temporal.io/sdk/workflow" ) // RetryPolicy defines exponential backoff retry behavior type RetryPolicy struct { // InitialInterval is the first wait duration InitialInterval time.Duration // MaximumInterval is the max wait duration between retries MaximumInterval time.Duration // BackoffCoefficient is the multiplier for each retry BackoffCoefficient float64 // MaximumAttempts is the max number of retries (0 = unlimited) MaximumAttempts int32 } // DefaultRetryPolicy returns a sensible default retry policy func DefaultRetryPolicy() *RetryPolicy { return &RetryPolicy{ InitialInterval: time.Second, MaximumInterval: time.Minute, BackoffCoefficient: 2.0, MaximumAttempts: 5, } } // ActivityRetryPolicy returns a retry policy for activities func ActivityRetryPolicy() *RetryPolicy { return &RetryPolicy{ InitialInterval: 2 * time.Second, MaximumInterval: 5 * time.Minute, BackoffCoefficient: 2.0, MaximumAttempts: 3, } } // LLMActivityRetryPolicy returns a retry policy for LLM activities (more lenient) func LLMActivityRetryPolicy() *RetryPolicy { return &RetryPolicy{ InitialInterval: 5 * time.Second, MaximumInterval: 10 * time.Minute, BackoffCoefficient: 1.5, MaximumAttempts: 5, } } // ToTemporalRetryPolicy converts to Temporal SDK's RetryPolicy func (p *RetryPolicy) ToTemporalRetryPolicy() *temporal.RetryPolicy { if p == nil { return nil } return &temporal.RetryPolicy{ InitialInterval: p.InitialInterval, MaximumInterval: p.MaximumInterval, BackoffCoefficient: p.BackoffCoefficient, MaximumAttempts: p.MaximumAttempts, } } // ApplyRetryPolicy applies a retry policy to activity options func ApplyRetryPolicy(opts workflow.ActivityOptions, policy *RetryPolicy) workflow.ActivityOptions { if policy == nil { return opts } opts.RetryPolicy = policy.ToTemporalRetryPolicy() return opts } // IsRetryableError checks if an error is retryable func IsRetryableError(err error) bool { if err == nil { return false } // Temporal SDK errors that should not be retried if temporal.IsTimeoutError(err) { return true // Timeouts are usually retryable } if temporal.IsCanceledError(err) { return false // Canceled workflows should not be retried } if temporal.IsApplicationError(err) { // Application errors are retryable by default return true } // Generic errors are retryable return true } // RetryCount holds retry attempt information type RetryCount struct { Current int Maximum int } // CanRetry checks if we can retry func (rc *RetryCount) CanRetry() bool { if rc.Maximum == 0 { return true // Unlimited retries } return rc.Current < rc.Maximum } // Increment increments the retry count func (rc *RetryCount) Increment() { rc.Current++ }