Class: Wurk::Limiter::Base
- Inherits:
-
Object
- Object
- Wurk::Limiter::Base
- Defined in:
- lib/wurk/limiter/base.rb
Overview
Shared base for every limiter type. Holds the public introspection
contract documented in §1.5 — name / type / options / size / status /
reset / delete plus the within_limit(...) { ... } block. Subclasses
override the acquire path and the per-type metric/size methods.
status is uniform across types (#16): { used:, limit:, reset_at:, available? }. Subclasses supply the three values via build_status;
Concurrent additionally merges its metric counters.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Instance Method Summary collapse
-
#build_status(used:, limit:, reset_at:) ⇒ Object
protected
Assemble the uniform status hash.
- #delete ⇒ Object
-
#fingerprint ⇒ Object
Stable fingerprint for the limiter — Sidekiq 8.0+ switched from SHA1 → SHA256 (~10% larger encoding).
-
#initialize(name, register: true, **options) ⇒ Base
constructor
register:defaults true so constructing a limiter publishes its metadata +lmtr-listmembership. - #lua(name, keys:, argv:) ⇒ Object protected
- #meta_key ⇒ Object protected
-
#random_id ⇒ Object
protected
Stable random slot id (Concurrent) / nonce (Window).
-
#register! ⇒ Object
protected
Metadata HSET + EXPIRE +
lmtr-listSADD in a single round trip. - #reset ⇒ Object
- #serializable_options ⇒ Object protected
- #size ⇒ Object
- #state_keys ⇒ Object protected
-
#status ⇒ Object
Uniform across types (#16).
- #ttl ⇒ Object protected
- #type ⇒ Object
- #within_limit ⇒ Object
Constructor Details
#initialize(name, register: true, **options) ⇒ Base
register: defaults true so constructing a limiter publishes its
metadata + lmtr-list membership. The dashboard reconstructs limiters
purely to read status on a GET, so it passes register: false to
keep introspection side-effect-free.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/wurk/limiter/base.rb', line 24 def initialize(name, register: true, **) unless name.is_a?(String) && NAME_PATTERN.match?(name) raise ArgumentError, "limiter name must match #{NAME_PATTERN.inspect} (got #{name.inspect})" end ttl = [:ttl] || DEFAULT_TTL # Spec §1.2: ttl floor of 24h. Anything tighter risks losing the # metadata hash mid-job and orphaning slots that read it. raise ArgumentError, 'ttl must be >= 86_400' if ttl < 86_400 @name = name.dup.freeze @options = register! if register end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
18 19 20 |
# File 'lib/wurk/limiter/base.rb', line 18 def name @name end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
18 19 20 |
# File 'lib/wurk/limiter/base.rb', line 18 def @options end |
Instance Method Details
#build_status(used:, limit:, reset_at:) ⇒ Object (protected)
Assemble the uniform status hash. available? is derived: an
unlimited (nil) limit is always available; otherwise headroom remains
while used < limit. reset_at is an epoch-seconds Float (or nil
when the type has no clock-driven reset).
84 85 86 87 |
# File 'lib/wurk/limiter/base.rb', line 84 def build_status(used:, limit:, reset_at:) { used: used, limit: limit, reset_at: reset_at, available?: limit.nil? || used < limit } end |
#delete ⇒ Object
63 64 65 66 67 68 |
# File 'lib/wurk/limiter/base.rb', line 63 def delete Wurk::Limiter.redis do |c| (state_keys + []).each { |k| c.call('DEL', k) } c.call('SREM', LIST_KEY, @name) end end |
#fingerprint ⇒ Object
Stable fingerprint for the limiter — Sidekiq 8.0+ switched from
SHA1 → SHA256 (~10% larger encoding). Web UI groups limiters by
this so that interpolated names (stripe-#{user_id}) get a single
row per shape.
74 75 76 |
# File 'lib/wurk/limiter/base.rb', line 74 def fingerprint @fingerprint ||= Digest::SHA256.hexdigest("#{type}|#{@name}|#{JSON.dump()}") end |
#lua(name, keys:, argv:) ⇒ Object (protected)
129 130 131 132 133 |
# File 'lib/wurk/limiter/base.rb', line 129 def lua(name, keys:, argv:) Wurk::Limiter.redis do |c| Wurk::Lua::Loader.eval_cached(c, name, keys: keys, argv: argv) end end |
#meta_key ⇒ Object (protected)
89 90 91 |
# File 'lib/wurk/limiter/base.rb', line 89 def "lmtr:#{@name}" end |
#random_id ⇒ Object (protected)
Stable random slot id (Concurrent) / nonce (Window). 16 hex chars
8 bytes — wide enough to avoid collision under any realistic
burst and short enough to keep ZSET memory bounded.
125 126 127 |
# File 'lib/wurk/limiter/base.rb', line 125 def random_id SecureRandom.hex(8) end |
#register! ⇒ Object (protected)
Metadata HSET + EXPIRE + lmtr-list SADD in a single round trip. The
script also gates the SADD on the metadata being newly written, so a
per-job construction (stripe-#{user_id}) stops rewriting a set that
grows one member per name — see limiter_register.lua for why that gate
is only safe while it is atomic with the HSET.
112 113 114 115 116 |
# File 'lib/wurk/limiter/base.rb', line 112 def register! lua(:limiter_register, keys: [, LIST_KEY], argv: [@name, type.to_s, JSON.dump(), fingerprint, ttl]) end |
#reset ⇒ Object
57 58 59 60 61 |
# File 'lib/wurk/limiter/base.rb', line 57 def reset Wurk::Limiter.redis do |c| state_keys.each { |k| c.call('DEL', k) } end end |
#serializable_options ⇒ Object (protected)
97 98 99 100 101 102 103 104 105 |
# File 'lib/wurk/limiter/base.rb', line 97 def @options.transform_values do |v| case v when Proc then '<proc>' when Symbol, Numeric, String, true, false, nil then v else v.to_s end end end |
#size ⇒ Object
47 48 49 |
# File 'lib/wurk/limiter/base.rb', line 47 def size 0 end |
#state_keys ⇒ Object (protected)
93 94 95 |
# File 'lib/wurk/limiter/base.rb', line 93 def state_keys [] end |
#status ⇒ Object
Uniform across types (#16). Subclasses override to fill in real numbers; the default reports an idle, unlimited shape.
53 54 55 |
# File 'lib/wurk/limiter/base.rb', line 53 def status build_status(used: 0, limit: nil, reset_at: nil) end |
#ttl ⇒ Object (protected)
118 119 120 |
# File 'lib/wurk/limiter/base.rb', line 118 def ttl @options[:ttl] end |
#type ⇒ Object
39 40 41 |
# File 'lib/wurk/limiter/base.rb', line 39 def type raise NotImplementedError end |
#within_limit ⇒ Object
43 44 45 |
# File 'lib/wurk/limiter/base.rb', line 43 def within_limit(**, &) raise NotImplementedError end |