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, retry_policy: nil, name: nil) ⇒ nil
Executes a method with automatic retry logic and durable execution tracking.
Instance Method Details
#durably_execute(method, retry_policy: nil, 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 retried per the resolved RetryPolicy
-
Backoff and attempt cap come from that policy (see RetryPolicy)
-
After the policy’s 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
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 65 def durably_execute(method, retry_policy: nil, name: nil) policy = step_retry_policy(retry_policy) validate_step_name_segment!(name || method) step_name = "durably_execute$#{name || method}" # Find or create execution log execution_log = find_or_create_execution_log!(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, execution_log: execution_log) # Optional retry logic backoff = policy.retry_backoff(e, attempts: execution_log.attempts) do |policy_key| bump_retry_count!(execution_log, policy_key) end if backoff # Reschedule with the policy's backoff (published after lock release). # The workflow replays on resume and skips completed steps, so the # rescheduled run picks this step up again by its execution log. enqueue_continuation(wait: backoff) # 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 |