Module: BreakerMachines::AsyncSupport

Extended by:
ActiveSupport::Concern
Defined in:
lib/breaker_machines/async_support.rb

Overview

AsyncSupport provides fiber-safe execution capabilities using the async gem

Instance Method Summary collapse

Instance Method Details

#async_timeout_error_classObject

Returns the Async::TimeoutError class if available



15
16
17
# File 'lib/breaker_machines/async_support.rb', line 15

def async_timeout_error_class
  ::Async::TimeoutError
end

#execute_call_asyncObject

Execute a call with async support (fiber-safe mode)



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
# File 'lib/breaker_machines/async_support.rb', line 20

def execute_call_async(&)
  start_time = monotonic_time

  begin
    # Execute with hedged requests if enabled
    result = if @config[:hedged_requests] || @config[:backends]
               execute_hedged(&)
             else
               execute_with_async_timeout(@config[:timeout], &)
             end

    record_success(monotonic_time - start_time)
    handle_success
    result
  rescue StandardError => e
    # Re-raise if it's not an async timeout or configured exception
    raise unless e.is_a?(async_timeout_error_class) || @config[:exceptions].any? { |klass| e.is_a?(klass) }

    record_failure(monotonic_time - start_time, e)
    handle_failure
    raise unless @config[:fallback]

    invoke_fallback_with_async(e)
  end
end

#execute_with_async_timeout(timeout) ⇒ Object

Execute a block with optional timeout using Async



47
48
49
50
51
52
53
54
# File 'lib/breaker_machines/async_support.rb', line 47

def execute_with_async_timeout(timeout, &)
  if timeout
    # Use safe, cooperative timeout from async gem
    ::Async::Task.current.with_timeout(timeout, &)
  else
    yield
  end
end

#invoke_fallback_with_async(error) ⇒ Object

Invoke fallback in async context



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/breaker_machines/async_support.rb', line 57

def invoke_fallback_with_async(error)
  case @config[:fallback]
  when BreakerMachines::DSL::ParallelFallbackWrapper
    invoke_parallel_fallbacks(@config[:fallback].fallbacks, error)
  when Proc
    result = if @config[:owner]
               @config[:owner].instance_exec(error, &@config[:fallback])
             else
               @config[:fallback].call(error)
             end

    # If the fallback returns an Async::Task, wait for it
    result.is_a?(::Async::Task) ? result.wait : result
  when Array
    # Try each fallback in order until one succeeds
    last_error = error
    @config[:fallback].each do |fallback|
      return invoke_single_fallback_async(fallback, last_error)
    rescue StandardError => e
      last_error = e
    end
    raise last_error
  else
    # Static values (strings, hashes, etc.) or Symbol fallbacks
    @config[:fallback]
  end
end