Module: GenevaDrive
- Defined in:
- lib/geneva_drive.rb,
lib/geneva_drive/version.rb,
lib/generators/geneva_drive/install/install_generator.rb
Overview
GenevaDrive provides durable workflows for Rails applications.
It offers a clean DSL for defining multi-step workflows that execute asynchronously, with strong guarantees around idempotency, concurrency control, and state management.
Defined Under Namespace
Modules: FlowControl, Generators, JobOptions, MigrationHelpers, TestHelpers Classes: CombinedExceptionPolicy, CursorTooLargeError, Engine, ExceptionPolicy, Executor, FlowControlSignal, HousekeepingJob, InvalidStateError, InvalidStateTransition, IterableStep, PerformStepJob, PreconditionError, RemovedStepDefinition, ResumableStepDefinition, Signal, SignalMatcher, SignalPayloadTooLargeError, StepCollection, StepConfigurationError, StepDefinition, StepExecution, StepExecutionError, StepFailedError, StepNotDefinedError, Workflow, WorkflowNotOngoing
Constant Summary collapse
- VERSION =
"0.7.0"
Class Attribute Summary collapse
-
.delete_completed_workflows_after ⇒ ActiveSupport::Duration?
How long to keep completed workflows before cleanup.
-
.enqueue_after_commit ⇒ Boolean
Whether to defer job enqueueing to after the database transaction commits.
-
.housekeeping_batch_size ⇒ Integer
Maximum number of workflows to process in a single housekeeping run.
-
.max_cursor_size ⇒ Integer?
Maximum size in bytes of a resumable step cursor once serialized to JSON.
-
.max_signal_payload_size ⇒ Integer?
Maximum size in bytes of a signal payload once serialized to JSON.
-
.stuck_in_progress_threshold ⇒ ActiveSupport::Duration
How long a step execution can be in "in_progress" state before being considered stuck and eligible for recovery.
-
.stuck_recovery_action ⇒ Symbol
Default recovery action for stuck step executions.
-
.stuck_scheduled_threshold ⇒ ActiveSupport::Duration
How long a step execution can be past its scheduled_for time while still in "scheduled" state before being considered stuck.
-
.waiting_visibility_threshold ⇒ ActiveSupport::Duration?
How long a step execution may sit in the "waiting" state before housekeeping counts it in the
geneva_drive.waiting_overduegauge.
Class Method Summary collapse
-
.enqueues_deferred? ⇒ Boolean
private
Returns true if job enqueueing should be deferred to after transaction commit.
-
.with_inline_enqueue { ... } ⇒ Object
Temporarily enables inline job enqueueing for the duration of the block.
Class Attribute Details
.delete_completed_workflows_after ⇒ ActiveSupport::Duration?
How long to keep completed workflows before cleanup. Set to nil to disable automatic cleanup.
64 65 66 |
# File 'lib/geneva_drive.rb', line 64 def delete_completed_workflows_after @delete_completed_workflows_after end |
.enqueue_after_commit ⇒ Boolean
Whether to defer job enqueueing to after the database transaction commits.
When true (the default in non-test environments), jobs are enqueued inside
an after_all_transactions_commit callback to ensure the step execution
record is visible to the job worker.
When false (the default in test environments), jobs are enqueued immediately. This is necessary for transactional tests (especially with SQLite) where the outermost test transaction never commits, causing after_commit callbacks to misbehave or not fire at all.
123 124 125 |
# File 'lib/geneva_drive.rb', line 123 def enqueue_after_commit @enqueue_after_commit end |
.housekeeping_batch_size ⇒ Integer
Maximum number of workflows to process in a single housekeeping run. Prevents runaway processing if there's a large backlog.
79 80 81 |
# File 'lib/geneva_drive.rb', line 79 def housekeeping_batch_size @housekeeping_batch_size end |
.max_cursor_size ⇒ Integer?
Maximum size in bytes of a resumable step cursor once serialized to JSON. The cursor is meant to be a compact position marker (an id, a page number, a pagination token) - not a payload. It is rewritten on every checkpoint and copied to every successor execution, so an oversized cursor multiplies write load and row size. Exceeding the limit raises CursorTooLargeError from set!/checkpoint!. Set to nil to disable the check.
94 95 96 |
# File 'lib/geneva_drive.rb', line 94 def max_cursor_size @max_cursor_size end |
.max_signal_payload_size ⇒ Integer?
Maximum size in bytes of a signal payload once serialized to JSON. A payload describes an event - an order id, an amount, a decision - not the data the step is going to work on. Exceeding the limit raises SignalPayloadTooLargeError from Workflow#signal!, before anything is persisted. Set to nil to disable the check.
102 103 104 |
# File 'lib/geneva_drive.rb', line 102 def max_signal_payload_size @max_signal_payload_size end |
.stuck_in_progress_threshold ⇒ ActiveSupport::Duration
How long a step execution can be in "in_progress" state before being considered stuck and eligible for recovery.
69 70 71 |
# File 'lib/geneva_drive.rb', line 69 def stuck_in_progress_threshold @stuck_in_progress_threshold end |
.stuck_recovery_action ⇒ Symbol
Default recovery action for stuck step executions. Can be :reattempt or :cancel
84 85 86 |
# File 'lib/geneva_drive.rb', line 84 def stuck_recovery_action @stuck_recovery_action end |
.stuck_scheduled_threshold ⇒ ActiveSupport::Duration
How long a step execution can be past its scheduled_for time while still in "scheduled" state before being considered stuck.
74 75 76 |
# File 'lib/geneva_drive.rb', line 74 def stuck_scheduled_threshold @stuck_scheduled_threshold end |
.waiting_visibility_threshold ⇒ ActiveSupport::Duration?
How long a step execution may sit in the "waiting" state before
housekeeping counts it in the geneva_drive.waiting_overdue gauge.
Waiting indefinitely is legitimate, so this is not a timeout - it is
the floor that keeps a stalled rendezvous from being silent.
Set to nil to disable the gauge.
110 111 112 |
# File 'lib/geneva_drive.rb', line 110 def waiting_visibility_threshold @waiting_visibility_threshold end |
Class Method Details
.enqueues_deferred? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns true if job enqueueing should be deferred to after transaction commit.
186 187 188 189 |
# File 'lib/geneva_drive.rb', line 186 def enqueues_deferred? return false if Thread.current[:geneva_drive_inline_enqueue] enqueue_after_commit end |
.with_inline_enqueue { ... } ⇒ Object
Temporarily enables inline job enqueueing for the duration of the block. Jobs will be enqueued immediately instead of waiting for transaction commit.
When to use this
This method is useful when creating multiple workflows inside a bulk
enqueueing context (e.g., BulkEnqueue.in_bulk). Normally, GenevaDrive
defers job enqueueing to after_all_transactions_commit callbacks.
However, these callbacks fire after the bulk enqueue block ends and
clears its buffer, causing jobs to be enqueued individually instead of
in bulk.
Requirements
Only use this if you have a co-committing, database-backed ActiveJob
adapter (such as SolidQueue, GoodJob, or Gouda) running on the same
database as your application models. With such adapters, the job INSERT
and the workflow/step_execution records are written in the same database
transaction, guaranteeing atomicity without needing after_commit deferral.
If you use a non-transactional queue adapter (Redis-based Sidekiq, Resque, etc.), do NOT use this method in production — jobs may be enqueued before their associated records are visible, causing "record not found" errors.
Thread safety
Uses a thread-local flag that won't affect other threads or requests. Safe to use in multi-threaded web servers (Puma, etc.).
174 175 176 177 178 179 180 |
# File 'lib/geneva_drive.rb', line 174 def with_inline_enqueue previous = Thread.current[:geneva_drive_inline_enqueue] Thread.current[:geneva_drive_inline_enqueue] = true yield ensure Thread.current[:geneva_drive_inline_enqueue] = previous end |