Module: Wurk::Web::Enterprise::Limits
- Defined in:
- lib/wurk/web/enterprise.rb
Overview
Limits tab — list every registered limiter, filter by name, expose
reset + delete. List/metrics already render via Wurk::Limiter::Base;
this wrapper adds the name filter documented in §1.2.0+.
Constant Summary collapse
- SWEEP_BATCH =
Names per sweep round trip. Bounds both halves: the probe's pipeline (whose replies the client buffers) and the removal script, which is atomic — an unbatched pass over a set that leaked for months would block every other Redis client for its whole length. A healthy set fits in one slice, so the common case still costs one probe.
512
Class Method Summary collapse
-
.list(filter: nil) ⇒ Array<String>
Listing is also the sweep.
- .meta_key(name) ⇒ Object
- .metadata(name) ⇒ Object
- .partition_live(names) ⇒ Object
-
.rebuild(name) ⇒ Object
Read-only reconstruction (
register: false) from the persisted metadata. -
.reset(name) ⇒ Object
Stats-key reset: drops every
lmtr-stats:/ state key for the named limiter while keeping its metadata + LIST membership so the row remains in the UI. -
.reset_by_prefix(name) ⇒ Object
Metadata-less fallback: wipe every type's un-suffixed state key.
-
.sweep(names) ⇒ Object
Drop the names whose metadata is gone, return the ones still live.
-
.sweep_batch(names) ⇒ Object
Claims apply-safety, unlike ProcessSet's comparable prune: the script re-decides liveness inside its own atomic step, so a replay can only remove names that are still dead.
- .sweep_slice(names) ⇒ Object
Class Method Details
.list(filter: nil) ⇒ Array<String>
Listing is also the sweep. lmtr-list membership has no TTL of its
own while lmtr:<name> expires with the limiter's ttl, so every
interpolated name (stripe-#{user_id}) the spec blesses would leave a
permanent member behind. Names whose metadata is gone are dropped from
the SET and from the result, so the dashboard also stops rendering a
row with nothing behind it. The liveness probe rides along with the
SMEMBERS the listing already pays for — both are O(set) — and once the
sweep has run the set is back down to live limiters.
43 44 45 46 47 48 49 |
# File 'lib/wurk/web/enterprise.rb', line 43 def list(filter: nil) names = sweep(Wurk.redis(idempotent: true) { |c| c.call('SMEMBERS', Wurk::Limiter::LIST_KEY) }).sort return names if filter.nil? || filter.to_s.empty? needle = filter.to_s.downcase names.select { |n| n.downcase.include?(needle) } end |
.meta_key(name) ⇒ Object
87 88 89 |
# File 'lib/wurk/web/enterprise.rb', line 87 def (name) "lmtr:#{name}" end |
.metadata(name) ⇒ Object
82 83 84 85 |
# File 'lib/wurk/web/enterprise.rb', line 82 def (name) raw = Wurk.redis(idempotent: true) { |c| c.call('HGETALL', (name)) } raw.is_a?(Array) ? raw.each_slice(2).to_h : raw end |
.partition_live(names) ⇒ Object
62 63 64 65 66 67 |
# File 'lib/wurk/web/enterprise.rb', line 62 def partition_live(names) flags = Wurk.redis(idempotent: true) do |c| c.pipelined { |pipe| names.each { |n| pipe.call('EXISTS', (n)) } } end names.partition.with_index { |_, i| flags[i].to_i == 1 } end |
.rebuild(name) ⇒ Object
Read-only reconstruction (register: false) from the persisted
metadata. Returns nil when the meta HASH is gone or malformed — the
name a caller holds came from a page list rendered earlier, and the
metadata can expire between the two.
108 109 110 111 112 113 114 115 116 |
# File 'lib/wurk/web/enterprise.rb', line 108 def rebuild(name) = (name) return nil if .nil? || .empty? = ['options'] ? ::JSON.parse(['options']) : {} Wurk::Limiter.build(name, ['type'], ) rescue StandardError nil end |
.reset(name) ⇒ Object
Stats-key reset: drops every lmtr-stats: / state key for the
named limiter while keeping its metadata + LIST membership so the
row remains in the UI. Mirrors the §1.5 #reset surface — the
name is wire-compat, so the trailing-? rule doesn't apply here.
95 96 97 98 99 100 101 102 |
# File 'lib/wurk/web/enterprise.rb', line 95 def reset(name) # rubocop:disable Naming/PredicateMethod limiter = rebuild(name) # Reconstruct the limiter and delegate: only the type itself knows # its state keys. A bucket's counter lives at `lmtr-b:<name>:<epoch>`, # so the bare-prefix DEL below never touched it. limiter ? limiter.reset : reset_by_prefix(name) true end |
.reset_by_prefix(name) ⇒ Object
Metadata-less fallback: wipe every type's un-suffixed state key.
119 120 121 122 123 124 |
# File 'lib/wurk/web/enterprise.rb', line 119 def reset_by_prefix(name) Wurk.redis do |c| %W[lmtr-cs:#{name} lmtr-w:#{name} lmtr-l:#{name} lmtr-p:#{name} lmtr-stats:#{name}].each { |k| c.call('DEL', k) } end end |
.sweep(names) ⇒ Object
Drop the names whose metadata is gone, return the ones still live.
52 53 54 |
# File 'lib/wurk/web/enterprise.rb', line 52 def sweep(names) names.each_slice(SWEEP_BATCH).flat_map { |slice| sweep_slice(slice) } end |
.sweep_batch(names) ⇒ Object
Claims apply-safety, unlike ProcessSet's comparable prune: the script re-decides liveness inside its own atomic step, so a replay can only remove names that are still dead.
72 73 74 75 76 77 78 79 80 |
# File 'lib/wurk/web/enterprise.rb', line 72 def sweep_batch(names) Wurk.redis(idempotent: true) do |c| Wurk::Lua::Loader.eval_cached( c, :limiter_list_sweep, keys: [Wurk::Limiter::LIST_KEY], argv: ['lmtr:', *names] ) end end |
.sweep_slice(names) ⇒ Object
56 57 58 59 60 |
# File 'lib/wurk/web/enterprise.rb', line 56 def sweep_slice(names) live, dead = partition_live(names) sweep_batch(dead) unless dead.empty? live end |