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



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

def async_timeout_error_class
  ::Async::TimeoutError
end

#execute_call_asyncObject

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



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/breaker_machines/async_support.rb', line 21

def execute_call_async(&)
  start_time = BreakerMachines.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(BreakerMachines.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(BreakerMachines.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 modern Async API



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

def execute_with_async_timeout(timeout, &)
  if timeout
    # Use modern timeout API - the flexible with_timeout API is on the task level
    Async::Task.current.with_timeout(timeout, &)
  else
    yield
  end
end

#invoke_fallback_with_async(error) ⇒ Object

Invoke fallback in async context



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

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