@@ -2,9 +2,6 @@ defmodule Lightning.WebhookRateLimiter do
2
2
@ moduledoc false
3
3
use GenServer
4
4
5
- @ capacity 10
6
- @ refill_per_sec 2
7
-
8
5
require Logger
9
6
10
7
def child_spec ( opts ) do
@@ -17,31 +14,36 @@ defmodule Lightning.WebhookRateLimiter do
17
14
18
15
% {
19
16
id: id ,
20
- start: { __MODULE__ , :start_link , [ name ] } ,
17
+ start: { __MODULE__ , :start_link , [ Keyword . put ( opts , : name, name ) ] } ,
21
18
shutdown: 10_000 ,
22
19
restart: :transient
23
20
}
24
21
end
25
22
26
- def start_link ( name ) do
23
+ def start_link ( opts ) do
24
+ name = Keyword . fetch! ( opts , :name )
25
+
27
26
with { :error , { :already_started , pid } } <-
28
- GenServer . start_link ( __MODULE__ , [ ] , name: via_tuple ( name ) ) do
27
+ GenServer . start_link ( __MODULE__ , opts , name: via_tuple ( name ) ) do
29
28
Logger . info ( "already started at #{ inspect ( pid ) } , returning :ignore" )
30
29
:ignore
31
30
end
32
31
end
33
32
34
33
@ impl true
35
- def init ( [ ] ) do
34
+ def init ( opts ) do
36
35
Process . flag ( :trap_exit , true )
37
36
38
- { :ok , % { table: :ets . new ( :table , [ :set ] ) } }
37
+ capacity = Keyword . fetch! ( opts , :capacity )
38
+ refill = Keyword . fetch! ( opts , :refill_per_second )
39
+
40
+ { :ok , % { table: :ets . new ( :table , [ :set ] ) , capacity: capacity , refill_per_second: refill } }
39
41
end
40
42
41
- def check_rate ( bucket , cost \\ 1 , name \\ __MODULE__ ) do
43
+ def check_rate ( bucket , capacity \\ nil , refill \\ nil , name \\ __MODULE__ ) do
42
44
name
43
45
|> via_tuple ( )
44
- |> GenServer . call ( { :check_rate , bucket , cost } )
46
+ |> GenServer . call ( { :check_rate , bucket , capacity , refill } )
45
47
end
46
48
47
49
def inspect_table ( name \\ __MODULE__ ) do
@@ -51,8 +53,8 @@ defmodule Lightning.WebhookRateLimiter do
51
53
end
52
54
53
55
@ impl true
54
- def handle_call ( { :check_rate , bucket , cost } , _from , % { table: table } = state ) do
55
- { :reply , do_check_rate ( table , bucket , cost ) , state }
56
+ def handle_call ( { :check_rate , bucket , capacity , refill } , _from , state ) do
57
+ { :reply , do_check_rate ( state , bucket , capacity , refill ) , state }
56
58
end
57
59
58
60
@ impl true
@@ -72,17 +74,24 @@ defmodule Lightning.WebhookRateLimiter do
72
74
{ :stop , :normal , state }
73
75
end
74
76
75
- def do_check_rate ( table , bucket , cost ) do
77
+ def do_check_rate (
78
+ % { table: table } = config ,
79
+ bucket ,
80
+ capacity ,
81
+ refill_per_sec
82
+ ) do
76
83
now = System . monotonic_time ( :millisecond )
84
+ capacity = capacity || config [ :capacity ]
85
+ refill_per_sec = refill_per_sec || config [ :refill_per_second ]
77
86
78
- :ets . insert_new ( table , { bucket , { @ capacity , now } } )
87
+ :ets . insert_new ( table , { bucket , { capacity , now } } )
79
88
[ { ^ bucket , { level , updated } } ] = :ets . lookup ( table , bucket )
80
89
81
- refilled = div ( now - updated , 1_000 ) * @ refill_per_sec
82
- current = min ( @ capacity , level + refilled )
90
+ refilled = div ( now - updated , 1_000 ) * refill_per_sec
91
+ current = min ( capacity , level + refilled )
83
92
84
- if current >= cost do
85
- level = current - cost
93
+ if current >= 1 do
94
+ level = current - 1
86
95
:ets . insert ( table , { bucket , { level , now } } )
87
96
88
97
{ :allow , level }
@@ -92,9 +101,6 @@ defmodule Lightning.WebhookRateLimiter do
92
101
end
93
102
end
94
103
95
- def capacity , do: @ capacity
96
- def refill_per_second , do: @ refill_per_sec
97
-
98
104
def via_tuple ( name ) ,
99
105
do: { :via , Horde.Registry , { Lightning.HordeRegistry , name } }
100
106
end
0 commit comments