@@ -34,6 +34,7 @@ defmodule Realtime.RateCounter do
34
34
tick_ref: nil ,
35
35
idle_shutdown: @ idle_shutdown ,
36
36
idle_shutdown_ref: nil ,
37
+ limit: % { log: false } ,
37
38
telemetry: % {
38
39
event_name: [ @ app_name ] ++ [ :rate_counter ] ,
39
40
measurements: % { sum: 0 } ,
@@ -49,6 +50,12 @@ defmodule Realtime.RateCounter do
49
50
tick_ref: reference ( ) ,
50
51
idle_shutdown: integer ( ) | :infinity ,
51
52
idle_shutdown_ref: reference ( ) ,
53
+ limit: % {
54
+ log: boolean ( ) ,
55
+ value: integer ( ) ,
56
+ triggered: boolean ( ) ,
57
+ log_fn: ( -> term ( ) )
58
+ } ,
52
59
telemetry: % {
53
60
emit: false ,
54
61
event_name: :telemetry . event_name ( ) ,
@@ -133,6 +140,8 @@ defmodule Realtime.RateCounter do
133
140
every = Keyword . get ( args , :tick , @ tick )
134
141
max_bucket_len = Keyword . get ( args , :max_bucket_len , @ max_bucket_len )
135
142
idle_shutdown_ms = Keyword . get ( args , :idle_shutdown , @ idle_shutdown )
143
+ limit_opts = Keyword . get ( args , :limit )
144
+
136
145
Logger . info ( "Starting #{ __MODULE__ } for: #{ inspect ( id ) } " )
137
146
138
147
# Always reset the counter in case the counter had already accumulated without
@@ -153,6 +162,18 @@ defmodule Realtime.RateCounter do
153
162
% { emit: false }
154
163
end
155
164
165
+ limit =
166
+ if limit_opts do
167
+ % {
168
+ log: true ,
169
+ value: limit_opts [ :value ] ,
170
+ log_fn: limit_opts [ :log_fn ] ,
171
+ triggered: false
172
+ }
173
+ else
174
+ % { log: false }
175
+ end
176
+
156
177
ticker = tick ( 0 )
157
178
158
179
idle_shutdown_ref =
@@ -165,7 +186,8 @@ defmodule Realtime.RateCounter do
165
186
max_bucket_len: max_bucket_len ,
166
187
idle_shutdown: idle_shutdown_ms ,
167
188
idle_shutdown_ref: idle_shutdown_ref ,
168
- telemetry: telemetry
189
+ telemetry: telemetry ,
190
+ limit: limit
169
191
}
170
192
171
193
Cachex . put! ( @ cache , id , state )
@@ -195,6 +217,8 @@ defmodule Realtime.RateCounter do
195
217
|> Kernel . / ( bucket_len )
196
218
197
219
state = % { state | bucket: bucket , avg: avg }
220
+
221
+ state = maybe_trigger_limit ( state )
198
222
tick ( state . tick )
199
223
200
224
Cachex . put! ( @ cache , state . id , state )
@@ -226,6 +250,28 @@ defmodule Realtime.RateCounter do
226
250
end
227
251
end
228
252
253
+ defp maybe_trigger_limit ( % { limit: % { log: false } } = state ) , do: state
254
+
255
+ defp maybe_trigger_limit ( % { limit: % { triggered: true } } = state ) do
256
+ # Limit has been triggered, but we need to check if it is still above the limit
257
+ if state . avg < state . limit . value do
258
+ % { state | limit: % { state . limit | triggered: false } }
259
+ else
260
+ # Limit is still above the threshold, so we keep the state as is
261
+ state
262
+ end
263
+ end
264
+
265
+ defp maybe_trigger_limit ( state ) do
266
+ if state . avg >= state . limit . value do
267
+ state . limit . log_fn . ( )
268
+
269
+ % { state | limit: % { state . limit | triggered: true } }
270
+ else
271
+ state
272
+ end
273
+ end
274
+
229
275
defp tick ( every ) do
230
276
Process . send_after ( self ( ) , :tick , every )
231
277
end
0 commit comments