Class: DiscordRDA::ExecutionSupervisor

Inherits:
Object
  • Object
show all
Defined in:
lib/discord_rda/core/execution_supervisor.rb

Defined Under Namespace

Classes: CircuitOpenError, ConcurrencyLimitError, IsolatedExecutionError, TimeoutError

Constant Summary collapse

DEFAULT_POLICY =
{
  timeout_seconds: 15,
  max_concurrency: 8,
  failure_threshold: 5,
  cooldown_seconds: 60
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(logger: nil) ⇒ ExecutionSupervisor

Returns a new instance of ExecutionSupervisor.



24
25
26
27
28
# File 'lib/discord_rda/core/execution_supervisor.rb', line 24

def initialize(logger: nil)
  @logger = logger
  @states = {}
  @mutex = Mutex.new
end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



22
23
24
# File 'lib/discord_rda/core/execution_supervisor.rb', line 22

def logger
  @logger
end

Instance Method Details

#execute(key, policy: {}, &block) ⇒ Object

Raises:



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/discord_rda/core/execution_supervisor.rb', line 30

def execute(key, policy: {}, &block)
  merged = DEFAULT_POLICY.merge(policy || {})
  state = state_for(key)

  raise CircuitOpenError, "Circuit open for #{key}" if circuit_open?(state)
  acquire_slot!(key, state, merged)

  begin
    result = ::Timeout.timeout(merged[:timeout_seconds]) { block.call }
    record_success(state)
    result
  rescue ::Timeout::Error => e
    record_failure(state)
    raise TimeoutError, e.message
  rescue StandardError
    record_failure(state)
    raise
  ensure
    release_slot(state)
  end
end

#run_isolated(ruby_code:, timeout_seconds: 15, memory_limit_mb: nil, env: {}) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/discord_rda/core/execution_supervisor.rb', line 52

def run_isolated(ruby_code:, timeout_seconds: 15, memory_limit_mb: nil, env: {})
  Tempfile.create(['discord_rda_isolated', '.rb']) do |file|
    file.write(build_isolated_runner(ruby_code, memory_limit_mb))
    file.flush

    stdout, stderr, status = spawn_with_timeout(
      [RbConfig.ruby, file.path],
      timeout_seconds: timeout_seconds,
      env: env
    )

    raise IsolatedExecutionError, stderr unless status.success?

    stdout
  end
end