@@ -10,36 +10,45 @@ defmodule Sentry.Transport.RateLimiter do
1010
1111 use GenServer
1212
13- @ table __MODULE__
14- @ sweep_interval_ms 60_000
13+ @ default_sweep_interval_ms 60_000
14+
15+ defstruct [ :table_name ]
1516
1617 ## Public API
1718
1819 @ doc """
1920 Starts the RateLimiter GenServer.
21+
22+ ## Options
23+
24+ * `:name` - The name to register the GenServer under. Defaults to `__MODULE__`.
25+ * `:table_name` - The name for the ETS table. Defaults to `__MODULE__`.
26+
2027 """
2128 @ spec start_link ( keyword ( ) ) :: GenServer . on_start ( )
2229 def start_link ( opts \\ [ ] ) do
23- GenServer . start_link ( __MODULE__ , nil , name: Keyword . get ( opts , :name , __MODULE__ ) )
30+ name = Keyword . get ( opts , :name , __MODULE__ )
31+ GenServer . start_link ( __MODULE__ , opts , name: name )
2432 end
2533
2634 ## GenServer Callbacks
2735
2836 @ impl true
29- def init ( nil ) do
30- _table = :ets . new ( @ table , [ :named_table , :public , :set , read_concurrency: true ] )
37+ def init ( opts ) do
38+ table_name = Keyword . get ( opts , :table_name , __MODULE__ )
39+ _table = :ets . new ( table_name , [ :named_table , :public , :set , read_concurrency: true ] )
3140 schedule_sweep ( )
32- { :ok , :no_state }
41+ { :ok , % __MODULE__ { table_name: table_name } }
3342 end
3443
3544 @ impl true
36- def handle_info ( :sweep , state ) do
45+ def handle_info ( :sweep , % __MODULE__ { table_name: table_name } = state ) do
3746 now = System . system_time ( :second )
3847
3948 # Match spec: select entries where expiry (position 2) < now
4049 match_spec = [ { { :"$1" , :"$2" } , [ { :< , :"$2" , now } ] , [ true ] } ]
4150
42- :ets . select_delete ( @ table , match_spec )
51+ :ets . select_delete ( table_name , match_spec )
4352
4453 schedule_sweep ( )
4554 { :noreply , state }
@@ -53,6 +62,10 @@ defmodule Sentry.Transport.RateLimiter do
5362 Returns `true` if the category is rate-limited (either specifically or via
5463 a global rate limit), `false` otherwise.
5564
65+ ## Options
66+
67+ * `:table_name` - The ETS table name. Defaults to `__MODULE__`.
68+
5669 ## Examples
5770
5871 iex> RateLimiter.rate_limited?("error")
@@ -63,10 +76,11 @@ defmodule Sentry.Transport.RateLimiter do
6376 true
6477
6578 """
66- @ spec rate_limited? ( String . t ( ) ) :: boolean ( )
67- def rate_limited? ( category ) do
79+ @ spec rate_limited? ( String . t ( ) , keyword ( ) ) :: boolean ( )
80+ def rate_limited? ( category , opts \\ [ ] ) do
81+ table_name = Keyword . get ( opts , :table_name , __MODULE__ )
6882 now = System . system_time ( :second )
69- check_rate_limited ( category , now ) or check_rate_limited ( :global , now )
83+ check_rate_limited ( table_name , category , now ) or check_rate_limited ( table_name , :global , now )
7084 end
7185
7286 @ doc """
@@ -75,17 +89,23 @@ defmodule Sentry.Transport.RateLimiter do
7589 This is a fallback for when X-Sentry-Rate-Limits is not present.
7690 Stores a global rate limit (:global key) that affects all categories.
7791
92+ ## Options
93+
94+ * `:table_name` - The ETS table name. Defaults to `__MODULE__`.
95+
7896 ## Examples
7997
8098 iex> RateLimiter.update_global_rate_limit(60)
8199 :ok
82100
83101 """
84- @ spec update_global_rate_limit ( pos_integer ( ) ) :: :ok
85- def update_global_rate_limit ( retry_after_seconds ) when is_integer ( retry_after_seconds ) do
102+ @ spec update_global_rate_limit ( pos_integer ( ) , keyword ( ) ) :: :ok
103+ def update_global_rate_limit ( retry_after_seconds , opts \\ [ ] )
104+ when is_integer ( retry_after_seconds ) do
105+ table_name = Keyword . get ( opts , :table_name , __MODULE__ )
86106 now = System . system_time ( :second )
87107 expiry = now + retry_after_seconds
88- :ets . insert ( @ table , { :global , expiry } )
108+ :ets . insert ( table_name , { :global , expiry } )
89109 :ok
90110 end
91111
@@ -95,30 +115,35 @@ defmodule Sentry.Transport.RateLimiter do
95115 Parses the header value and stores expiry timestamps for each category.
96116 Returns `:ok` regardless of parsing success.
97117
118+ ## Options
119+
120+ * `:table_name` - The ETS table name. Defaults to `__MODULE__`.
121+
98122 ## Examples
99123
100124 iex> RateLimiter.update_rate_limits("60:error;transaction")
101125 :ok
102126
103127 """
104- @ spec update_rate_limits ( String . t ( ) ) :: :ok
105- def update_rate_limits ( rate_limits_header ) do
128+ @ spec update_rate_limits ( String . t ( ) , keyword ( ) ) :: :ok
129+ def update_rate_limits ( rate_limits_header , opts \\ [ ] ) do
130+ table_name = Keyword . get ( opts , :table_name , __MODULE__ )
106131 now = System . system_time ( :second )
107132 rate_limits = parse_rate_limits_header ( rate_limits_header )
108133
109134 Enum . each ( rate_limits , fn { category , retry_after_seconds } ->
110135 expiry = now + retry_after_seconds
111- :ets . insert ( @ table , { category , expiry } )
136+ :ets . insert ( table_name , { category , expiry } )
112137 end )
113138
114139 :ok
115140 end
116141
117142 ## Private Helpers
118143
119- @ spec check_rate_limited ( String . t ( ) | :global , integer ( ) ) :: boolean ( )
120- defp check_rate_limited ( category , time ) do
121- case :ets . lookup ( @ table , category ) do
144+ @ spec check_rate_limited ( atom ( ) , String . t ( ) | :global , integer ( ) ) :: boolean ( )
145+ defp check_rate_limited ( table_name , category , time ) do
146+ case :ets . lookup ( table_name , category ) do
122147 [ { ^ category , expiry } ] when expiry > time -> true
123148 _ -> false
124149 end
@@ -167,6 +192,6 @@ defmodule Sentry.Transport.RateLimiter do
167192
168193 @ spec schedule_sweep ( ) :: reference ( )
169194 defp schedule_sweep do
170- Process . send_after ( self ( ) , :sweep , @ sweep_interval_ms )
195+ Process . send_after ( self ( ) , :sweep , @ default_sweep_interval_ms )
171196 end
172197end
0 commit comments