Module: RubyReactor::Dsl::Lockable::ClassMethods

Defined in:
lib/ruby_reactor/dsl/lockable.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#lock_configObject (readonly)

Returns the value of attribute lock_config.



11
12
13
# File 'lib/ruby_reactor/dsl/lockable.rb', line 11

def lock_config
  @lock_config
end

#period_configObject (readonly)

Returns the value of attribute period_config.



11
12
13
# File 'lib/ruby_reactor/dsl/lockable.rb', line 11

def period_config
  @period_config
end

#rate_limit_configObject (readonly)

Returns the value of attribute rate_limit_config.



11
12
13
# File 'lib/ruby_reactor/dsl/lockable.rb', line 11

def rate_limit_config
  @rate_limit_config
end

#semaphore_configObject (readonly)

Returns the value of attribute semaphore_config.



11
12
13
# File 'lib/ruby_reactor/dsl/lockable.rb', line 11

def semaphore_config
  @semaphore_config
end

Instance Method Details

#inherited(subclass) ⇒ Object

Propagate lock/semaphore/period/rate-limit config to subclasses; without this a subclass of a configured reactor would silently lose those settings.



16
17
18
19
20
21
22
# File 'lib/ruby_reactor/dsl/lockable.rb', line 16

def inherited(subclass)
  super
  subclass.instance_variable_set(:@lock_config, @lock_config) if @lock_config
  subclass.instance_variable_set(:@semaphore_config, @semaphore_config) if @semaphore_config
  subclass.instance_variable_set(:@period_config, @period_config) if @period_config
  subclass.instance_variable_set(:@rate_limit_config, @rate_limit_config) if @rate_limit_config
end

#with_lock(ttl: 60, wait: 0, auto_extend: true) {|inputs| ... } ⇒ Object

Configure locking for this reactor

Parameters:

  • ttl (Integer) (defaults to: 60)

    Time to live in seconds (default: 60)

  • wait (Integer) (defaults to: 0)

    Time to wait for lock in seconds (default: 0)

  • auto_extend (Boolean) (defaults to: true)

    When true (default), a background thread refreshes the lock TTL every ttl/3 seconds while the reactor runs, protecting steps that may legitimately outlast ‘ttl`. Pass `false` to disable and rely solely on `ttl` for expiry.

Yields:

  • (inputs)

    Block that returns the lock key string



32
33
34
35
36
37
38
39
# File 'lib/ruby_reactor/dsl/lockable.rb', line 32

def with_lock(ttl: 60, wait: 0, auto_extend: true, &block)
  @lock_config = {
    ttl: ttl,
    wait: wait,
    auto_extend: auto_extend,
    key_proc: block
  }
end

#with_period(every:) {|inputs| ... } ⇒ Object

Configure a calendar-aligned dedup window for this reactor. The reactor will run at most once per bucket per key; subsequent calls in the same bucket return ‘RubyReactor::Skipped` without executing any steps.

Note: ‘with_period` is dedup, not concurrency. Two concurrent racers can both see no marker and both run. Pair with `with_lock` for true at-most-one semantics within the bucket.

Parameters:

  • every (Symbol, Integer)

    :minute / :hour / :day / :week / :month / :year, or an integer number of seconds for a sliding bucket (index = ‘time.to_i / every`).

Yields:

  • (inputs)

    Block that returns the period key base. The final Redis marker key is ‘period:<base>:<bucket_id>`.



67
68
69
70
71
72
73
74
75
# File 'lib/ruby_reactor/dsl/lockable.rb', line 67

def with_period(every:, &block)
  # Validate eagerly so misconfiguration surfaces at class load time.
  RubyReactor::Period.period_seconds(every)

  @period_config = {
    every: every,
    key_proc: block
  }
end

#with_rate_limit(limit: nil, period: nil, limits: nil) {|inputs| ... } ⇒ Object

Configure rate limiting for this reactor (fixed-window counter). Pass either a single window via ‘limit:` + `period:`, or a hash of windows via `limits:` for layered API quotas.

Examples:

Single window

with_rate_limit(limit: 3, period: :second) { |i| "stripe:#{i[:account_id]}" }

Multi-window (3/sec AND 100/min AND 5000/hr)

with_rate_limit(
  limits: { second: 3, minute: 100, hour: 5000 }
) { |i| "stripe:#{i[:account_id]}" }

Parameters:

  • limit (Integer) (defaults to: nil)

    requests per period (single-window form)

  • period (Symbol, Integer) (defaults to: nil)

    :second / :minute / :hour / :day / :week / :month / :year, or integer seconds (single-window form)

  • limits (Hash{Symbol,Integer => Integer}) (defaults to: nil)

    mapping of period unit to limit (multi-window form)

Yields:

  • (inputs)

    Block returning the rate-limit key base.



95
96
97
98
99
100
101
102
# File 'lib/ruby_reactor/dsl/lockable.rb', line 95

def with_rate_limit(limit: nil, period: nil, limits: nil, &block)
  normalized = normalize_rate_limit_args(limit, period, limits)

  @rate_limit_config = {
    limits: normalized,
    key_proc: block
  }
end

#with_semaphore(limit:, wait: 0) {|inputs| ... } ⇒ Object

Configure semaphore for this reactor

Parameters:

  • limit (Integer)

    Maximum concurrent executions

  • wait (Integer) (defaults to: 0)

    Time to wait for a token in seconds (default: 0)

Yields:

  • (inputs)

    Block that returns the semaphore key string



45
46
47
48
49
50
51
# File 'lib/ruby_reactor/dsl/lockable.rb', line 45

def with_semaphore(limit:, wait: 0, &block)
  @semaphore_config = {
    limit: limit,
    wait: wait,
    key_proc: block
  }
end