Class: Findbug::Alerts::Throttler
- Inherits:
-
Object
- Object
- Findbug::Alerts::Throttler
- Defined in:
- lib/findbug/alerts/throttler.rb
Overview
Throttler prevents alert spam by limiting how often we alert for the same error.
THE PROBLEM
Without throttling:
-
1000 users hit the same bug
-
1000 Slack messages
-
Your team mutes the channel
-
You miss the NEXT important error
With throttling:
-
First occurrence: Alert sent
-
Next 999 in 5 minutes: Throttled
-
5 minutes later, if still happening: Another alert
IMPLEMENTATION
We use Redis to store “last alerted at” timestamps:
Key: findbug:alert:throttle:{fingerprint}
Value: ISO8601 timestamp
TTL: throttle_period
If the key exists and isn’t expired, we’re throttled. Simple and fast.
Constant Summary collapse
- THROTTLE_KEY_PREFIX =
"findbug:alert:throttle:"
Class Method Summary collapse
-
.clear(fingerprint) ⇒ Object
Clear throttle for a specific error (e.g., when error is resolved).
-
.record(fingerprint) ⇒ Object
Record that we sent an alert (starts throttle period).
-
.remaining_seconds(fingerprint) ⇒ Integer?
Get remaining throttle time.
-
.throttled?(fingerprint) ⇒ Boolean
Check if an alert is currently throttled.
Class Method Details
.clear(fingerprint) ⇒ Object
Clear throttle for a specific error (e.g., when error is resolved)
72 73 74 75 76 77 78 79 80 |
# File 'lib/findbug/alerts/throttler.rb', line 72 def clear(fingerprint) key = throttle_key(fingerprint) Storage::ConnectionPool.with do |redis| redis.del(key) end rescue StandardError # Ignore errors during cleanup end |
.record(fingerprint) ⇒ Object
Record that we sent an alert (starts throttle period)
57 58 59 60 61 62 63 64 65 66 |
# File 'lib/findbug/alerts/throttler.rb', line 57 def record(fingerprint) key = throttle_key(fingerprint) ttl = throttle_period Storage::ConnectionPool.with do |redis| redis.setex(key, ttl, Time.now.utc.iso8601) end rescue StandardError => e Findbug.logger.debug("[Findbug] Throttle record failed: #{e.}") end |
.remaining_seconds(fingerprint) ⇒ Integer?
Get remaining throttle time
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/findbug/alerts/throttler.rb', line 87 def remaining_seconds(fingerprint) key = throttle_key(fingerprint) Storage::ConnectionPool.with do |redis| ttl = redis.ttl(key) ttl.positive? ? ttl : nil end rescue StandardError nil end |
.throttled?(fingerprint) ⇒ Boolean
Check if an alert is currently throttled
42 43 44 45 46 47 48 49 50 51 |
# File 'lib/findbug/alerts/throttler.rb', line 42 def throttled?(fingerprint) key = throttle_key(fingerprint) Storage::ConnectionPool.with do |redis| redis.exists?(key) end rescue StandardError => e Findbug.logger.debug("[Findbug] Throttle check failed: #{e.}") false # If we can't check, allow the alert end |