Module: ChronoForge::Executor::Methods::DurablyExecute
- Included in:
- ChronoForge::Executor::Methods
- Defined in:
- lib/chrono_forge/executor/methods/durably_execute.rb
Instance Method Summary collapse
-
#durably_execute(method, max_attempts: 3, name: nil) ⇒ nil
Executes a method with automatic retry logic and durable execution tracking.
Instance Method Details
#durably_execute(method, max_attempts: 3, name: nil) ⇒ nil
Executes a method with automatic retry logic and durable execution tracking.
This method provides fault-tolerant execution of instance methods with automatic retry on failure using exponential backoff. Each execution is tracked with its own execution log, ensuring idempotent behavior during workflow replays.
Behavior
Idempotency
Each execution gets a unique step name ensuring that workflow replays don’t create duplicate executions. If a workflow is replayed and this step has already completed, it will be skipped.
Retry Logic
-
Failed executions are automatically retried with exponential backoff
-
Backoff calculation: 2^attempt seconds (capped at 2^5 = 32 seconds)
-
After max_attempts, ExecutionFailedError is raised
Error Handling
-
All exceptions except HaltExecutionFlow are caught and handled
-
Errors are logged and tracked in the execution log
-
ExecutionFailedError is raised after exhausting all retry attempts
-
HaltExecutionFlow exceptions are re-raised to allow workflow control flow
Execution Logs
Creates execution log with step name: ‘durably_execute$#|| method`
-
Tracks attempt count, execution times, and completion status
-
Stores error details when failures occur
-
Enables monitoring and debugging of execution history
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/chrono_forge/executor/methods/durably_execute.rb', line 62 def durably_execute(method, max_attempts: 3, name: nil) step_name = "durably_execute$#{name || method}" # Find or create execution log execution_log = ExecutionLog.create_or_find_by!( workflow: @workflow, step_name: step_name ) do |log| log.started_at = Time.current end # Return if already completed return if execution_log.completed? # Execute with error handling begin # Update execution log with attempt execution_log.update!( attempts: execution_log.attempts + 1, last_executed_at: Time.current ) # Execute the method send(method) # Complete the execution execution_log.update!( state: :completed, completed_at: Time.current ) # return nil nil rescue HaltExecutionFlow raise rescue => e # Log the error Rails.logger.error { "Error while durably executing #{method}: #{e.}" } self.class::ExecutionTracker.track_error(workflow, e) # Optional retry logic if execution_log.attempts < max_attempts # Reschedule with exponential backoff backoff = (2**[execution_log.attempts, 5].min).seconds self.class .set(wait: backoff) .perform_later( @workflow.key, retry_method: method ) # Halt current execution halt_execution! else # Max attempts reached execution_log.update!( state: :failed, error_message: e., error_class: e.class.name ) raise ExecutionFailedError, "#{step_name} failed after maximum attempts" end end end |