Module: RubyReactor

Defined in:
lib/ruby_reactor.rb,
lib/ruby_reactor/lock.rb,
lib/ruby_reactor/step.rb,
lib/ruby_reactor/rspec.rb,
lib/ruby_reactor/period.rb,
lib/ruby_reactor/worker.rb,
lib/ruby_reactor/context.rb,
lib/ruby_reactor/reactor.rb,
lib/ruby_reactor/sweeper.rb,
lib/ruby_reactor/version.rb,
lib/ruby_reactor/web/api.rb,
lib/ruby_reactor/executor.rb,
lib/ruby_reactor/registry.rb,
lib/ruby_reactor/semaphore.rb,
lib/ruby_reactor/error/base.rb,
lib/ruby_reactor/middleware.rb,
lib/ruby_reactor/rate_limit.rb,
lib/ruby_reactor/dsl/reactor.rb,
lib/ruby_reactor/map/helpers.rb,
lib/ruby_reactor/map/sweeper.rb,
lib/ruby_reactor/sweeper_job.rb,
lib/ruby_reactor/dsl/lockable.rb,
lib/ruby_reactor/ordered_lock.rb,
lib/ruby_reactor/configuration.rb,
lib/ruby_reactor/map/collector.rb,
lib/ruby_reactor/retry_context.rb,
lib/ruby_reactor/rspec/helpers.rb,
lib/ruby_reactor/step/map_step.rb,
lib/ruby_reactor/template/base.rb,
lib/ruby_reactor/map/dispatcher.rb,
lib/ruby_reactor/open_telemetry.rb,
lib/ruby_reactor/rspec/matchers.rb,
lib/ruby_reactor/template/input.rb,
lib/ruby_reactor/template/value.rb,
lib/ruby_reactor/dsl/map_builder.rb,
lib/ruby_reactor/storage/adapter.rb,
lib/ruby_reactor/template/result.rb,
lib/ruby_reactor/validation/base.rb,
lib/ruby_reactor/web/application.rb,
lib/ruby_reactor/dependency_graph.rb,
lib/ruby_reactor/dsl/step_builder.rb,
lib/ruby_reactor/error/undo_error.rb,
lib/ruby_reactor/interrupt_result.rb,
lib/ruby_reactor/template/element.rb,
lib/ruby_reactor/middleware_runner.rb,
lib/ruby_reactor/step/compose_step.rb,
lib/ruby_reactor/context_serializer.rb,
lib/ruby_reactor/rspec/test_subject.rb,
lib/ruby_reactor/dsl/compose_builder.rb,
lib/ruby_reactor/rate_limit_registry.rb,
lib/ruby_reactor/retry_queued_result.rb,
lib/ruby_reactor/rspec/storage_reset.rb,
lib/ruby_reactor/dsl/template_helpers.rb,
lib/ruby_reactor/map/element_executor.rb,
lib/ruby_reactor/utils/code_extractor.rb,
lib/ruby_reactor/dsl/interrupt_builder.rb,
lib/ruby_reactor/map/result_enumerator.rb,
lib/ruby_reactor/rspec/sidekiq_helpers.rb,
lib/ruby_reactor/storage/configuration.rb,
lib/ruby_reactor/storage/redis_adapter.rb,
lib/ruby_reactor/storage/redis_locking.rb,
lib/ruby_reactor/dsl/validation_helpers.rb,
lib/ruby_reactor/error/dependency_error.rb,
lib/ruby_reactor/error/validation_error.rb,
lib/ruby_reactor/executor/graph_manager.rb,
lib/ruby_reactor/executor/retry_manager.rb,
lib/ruby_reactor/executor/step_executor.rb,
lib/ruby_reactor/adapters/sidekiq/router.rb,
lib/ruby_reactor/adapters/sidekiq/worker.rb,
lib/ruby_reactor/executor/result_handler.rb,
lib/ruby_reactor/template/dynamic_source.rb,
lib/ruby_reactor/error/compensation_error.rb,
lib/ruby_reactor/error/step_failure_error.rb,
lib/ruby_reactor/executor/input_validator.rb,
lib/ruby_reactor/rspec/active_job_helpers.rb,
lib/ruby_reactor/rspec/async_test_helpers.rb,
lib/ruby_reactor/utils/backtrace_location.rb,
lib/ruby_reactor/dsl/interrupt_step_config.rb,
lib/ruby_reactor/rspec/step_executor_patch.rb,
lib/ruby_reactor/validation/schema_builder.rb,
lib/ruby_reactor/adapters/active_job/compat.rb,
lib/ruby_reactor/adapters/active_job/router.rb,
lib/ruby_reactor/adapters/active_job/worker.rb,
lib/ruby_reactor/error/schema_version_error.rb,
lib/ruby_reactor/validation/input_validator.rb,
lib/ruby_reactor/error/deserialization_error.rb,
lib/ruby_reactor/web/coordination_serializer.rb,
lib/ruby_reactor/error/input_validation_error.rb,
lib/ruby_reactor/error/context_too_large_error.rb,
lib/ruby_reactor/executor/compensation_manager.rb,
lib/ruby_reactor/executor/ordered_lock_support.rb,
lib/ruby_reactor/max_retries_exhausted_failure.rb,
lib/ruby_reactor/storage/redis_ordered_locking.rb,
lib/ruby_reactor/adapters/sidekiq/sweeper_worker.rb,
lib/ruby_reactor/adapters/active_job/sweeper_worker.rb,
lib/ruby_reactor/adapters/sidekiq/map_element_worker.rb,
lib/ruby_reactor/adapters/sidekiq/map_collector_worker.rb,
lib/ruby_reactor/adapters/active_job/map_element_worker.rb,
lib/ruby_reactor/adapters/active_job/map_collector_worker.rb,
sig/ruby_reactor.rbs

Defined Under Namespace

Modules: Adapters, Dsl, Error, Map, Period, RSpec, Step, Storage, SweeperJob, Template, Utils, Validation, Web, Worker Classes: AsyncResult, Configuration, Context, ContextSerializer, DependencyGraph, Executor, Failure, InterruptResult, Lock, MaxRetriesExhaustedFailure, Middleware, MiddlewareRunner, OpenTelemetry, OrderedLock, RateLimit, RateLimitRegistry, Reactor, Registry, RetryContext, RetryQueuedResult, Semaphore, Skipped, Success, Sweeper

Constant Summary collapse

VERSION =

Returns:

  • (String)
"0.6.0"

Class Method Summary collapse

Class Method Details

.configurationObject



337
338
339
# File 'lib/ruby_reactor.rb', line 337

def self.configuration
  Configuration.instance
end

.configure {|Configuration.instance| ... } ⇒ Object

Yields:



333
334
335
# File 'lib/ruby_reactor.rb', line 333

def self.configure
  yield(Configuration.instance) if block_given?
end

.Failure(error, **kwargs) ⇒ Object



323
324
325
# File 'lib/ruby_reactor.rb', line 323

def self.Failure(error, **kwargs)
  Failure.new(error, **kwargs)
end

.inferred_sweeper_job_class(router) ⇒ Object



386
387
388
389
390
391
392
393
# File 'lib/ruby_reactor.rb', line 386

def self.inferred_sweeper_job_class(router)
  namespace_name = router.name.to_s.rpartition("::").first
  raise sweeper_job_class_error(router) if namespace_name.empty?

  Object.const_get(namespace_name).const_get(:SweeperWorker)
rescue NameError
  raise sweeper_job_class_error(router)
end

.internal_lib_pathObject



418
419
420
# File 'lib/ruby_reactor.rb', line 418

def self.internal_lib_path
  File.join(root.to_s, "lib")
end

.reactor_storage_name(reactor_class) ⇒ Object

The name under which a reactor class's durable state is keyed in storage (reactor:<name>:context:<id>, map metadata, etc.). MUST be stable across processes: the enqueuing process writes the blob under this name and a different worker process reads it back by the same name. So an anonymous class falls back to a fixed constant, NOT object_id — object_id is process-local and would make the worker's read key miss the writer's key. The context_id in the key still disambiguates distinct anonymous reactors. (A truly anonymous class can't be reconstituted by name in another process, so cross-process resume of one is inherently unsupported; this only keeps the keys self-consistent within a process — e.g. inline tests.)



351
352
353
354
355
# File 'lib/ruby_reactor.rb', line 351

def self.reactor_storage_name(reactor_class)
  return "AnonymousReactor" if reactor_class.nil?

  reactor_class.name || "AnonymousReactor"
end

.rootObject



414
415
416
# File 'lib/ruby_reactor.rb', line 414

def self.root
  Pathname.new(File.expand_path("..", __dir__))
end

.Skipped(reason: nil, **kwargs) ⇒ Object

Build a Skipped result. Return one from a step's run block to halt the reactor cleanly without triggering compensation of previous steps.



329
330
331
# File 'lib/ruby_reactor.rb', line 329

def self.Skipped(reason: nil, **kwargs)
  Skipped.new(reason: reason, **kwargs)
end

.start_sweeper!Object

Kick the self-rescheduling recovery sweeper chain. Call once per cluster — typically from an initializer (RubyReactor.start_sweeper!). Idempotent: calling it on every process boot is safe because the worker claims each tick by time-window, so duplicate kicks collapse to a single chain. No-op when config.sweeper_enabled is false. Returns the scheduled job id, or nil when disabled or when this window's tick was already claimed by another caller.



363
364
365
366
367
# File 'lib/ruby_reactor.rb', line 363

def self.start_sweeper!
  return unless configuration.sweeper_enabled

  sweeper_job_class.schedule_next
end

.Success(value = nil) ⇒ Object

Global helper methods



319
320
321
# File 'lib/ruby_reactor.rb', line 319

def self.Success(value = nil)
  Success.new(value)
end

.sweep_once(limit: nil) ⇒ Object

Run both recovery sweepers exactly once and return their counts. The synchronous escape hatch for hosts that schedule recovery with their own cron / k8s CronJob instead of the in-cluster chain (set config.sweeper_enabled = false and call this from rake ruby_reactor:sweep or a binstub).



406
407
408
409
410
411
412
# File 'lib/ruby_reactor.rb', line 406

def self.sweep_once(limit: nil)
  limit ||= configuration.sweeper_limit
  {
    reactors: Sweeper.run_once(limit: limit),
    maps: Map::Sweeper.run_once(limit: limit)
  }
end

.sweeper_job_classObject

The sweeper job class living alongside the configured async_router (e.g. Adapters::Sidekiq::Router -> Adapters::Sidekiq::SweeperWorker), so the chain is kicked through whichever backend is configured instead of a hardcoded Sidekiq class. Known built-in routers are mapped explicitly; a custom configuration.async_router falls back to a sibling-namespace lookup, with a clear error (instead of a bare NameError) when that convention doesn't hold.



376
377
378
379
380
381
382
383
384
# File 'lib/ruby_reactor.rb', line 376

def self.sweeper_job_class
  router = configuration.async_router
  case router.name
  when "RubyReactor::Adapters::Sidekiq::Router" then Adapters::Sidekiq::SweeperWorker
  when "RubyReactor::Adapters::ActiveJob::Router" then Adapters::ActiveJob::SweeperWorker
  else
    inferred_sweeper_job_class(router)
  end
end

.sweeper_job_class_error(router) ⇒ Object



395
396
397
398
399
# File 'lib/ruby_reactor.rb', line 395

def self.sweeper_job_class_error(router)
  "RubyReactor: cannot infer a sweeper job class for custom async_router " \
    "#{router.inspect}. Define a `SweeperWorker` class alongside it " \
    "(same namespace), or override `RubyReactor.sweeper_job_class`."
end