mirror of
https://github.com/fluxerapp/fluxer.git
synced 2026-09-03 05:10:25 +03:00
fix(gateway): stop rate limit tables dying with their creator (#2299)
This commit is contained in:
@@ -320,18 +320,17 @@ create_counter_table(Table) ->
|
||||
-spec create_table(atom()) -> created | exists.
|
||||
create_table(Table) ->
|
||||
try
|
||||
_ = ets:new(Table, [
|
||||
named_table,
|
||||
public,
|
||||
set,
|
||||
{write_concurrency, true},
|
||||
{read_concurrency, true}
|
||||
]),
|
||||
_ = ets:new(Table, rate_table_options()),
|
||||
created
|
||||
catch
|
||||
error:badarg -> exists
|
||||
end.
|
||||
|
||||
-spec rate_table_options() -> list().
|
||||
rate_table_options() ->
|
||||
[named_table, public, set, {write_concurrency, true}, {read_concurrency, true}] ++
|
||||
guild_ets_utils:heir_options().
|
||||
|
||||
-spec rate_limits_disabled() -> boolean().
|
||||
rate_limits_disabled() ->
|
||||
case os:getenv("FLUXER_DISABLE_RATE_LIMITS") of
|
||||
@@ -502,6 +501,41 @@ await_table_size(Table, Max, Attempts) ->
|
||||
await_table_size(Table, Max, Attempts - 1)
|
||||
end.
|
||||
|
||||
shared_ip_bucket_survives_creating_process_death_test() ->
|
||||
with_rate_limits_enabled(fun assert_shared_ip_bucket_survives_creator/0).
|
||||
|
||||
assert_shared_ip_bucket_survives_creator() ->
|
||||
drop_table(?SHARED_IP_RATE_TABLE),
|
||||
{ok, _} = guild_ets_owner:start_link(),
|
||||
try
|
||||
assert_shared_ip_bucket_outlives_creator()
|
||||
after
|
||||
gen_server:stop(guild_ets_owner)
|
||||
end.
|
||||
|
||||
assert_shared_ip_bucket_outlives_creator() ->
|
||||
IP = <<"198.51.100.70">>,
|
||||
stop_table_owner(start_shared_ip_bucket_creator(IP)),
|
||||
?assertNotEqual(undefined, ets:whereis(?SHARED_IP_RATE_TABLE)),
|
||||
Key = {IP, erlang:system_time(millisecond) div ?SHARED_IP_RATE_WINDOW_MS},
|
||||
?assertEqual([{Key, 1}], ets:lookup(?SHARED_IP_RATE_TABLE, Key)).
|
||||
|
||||
start_shared_ip_bucket_creator(IP) ->
|
||||
Parent = self(),
|
||||
Pid = spawn(fun() -> create_shared_ip_bucket(Parent, IP) end),
|
||||
receive
|
||||
{owner_ready, Pid} -> Pid
|
||||
after 1000 -> error(owner_start_timeout)
|
||||
end.
|
||||
|
||||
create_shared_ip_bucket(Parent, IP) ->
|
||||
ok = check_shared_ip_rate(IP),
|
||||
Parent ! {owner_ready, self()},
|
||||
receive
|
||||
stop -> ok
|
||||
after 30000 -> ok
|
||||
end.
|
||||
|
||||
window_cleanup_loops_do_not_outlive_their_table_test() ->
|
||||
with_rate_limits_enabled(fun() ->
|
||||
with_fast_cleanup_timer(fun assert_window_cleanup_loops_do_not_leak/0)
|
||||
|
||||
@@ -151,18 +151,17 @@ create_session_user_counts_table() ->
|
||||
-spec create_rate_table(atom()) -> created | exists.
|
||||
create_rate_table(Table) ->
|
||||
try
|
||||
_ = ets:new(Table, [
|
||||
named_table,
|
||||
public,
|
||||
set,
|
||||
{write_concurrency, true},
|
||||
{read_concurrency, true}
|
||||
]),
|
||||
_ = ets:new(Table, rate_table_options()),
|
||||
created
|
||||
catch
|
||||
error:badarg -> exists
|
||||
end.
|
||||
|
||||
-spec rate_table_options() -> list().
|
||||
rate_table_options() ->
|
||||
[named_table, public, set, {write_concurrency, true}, {read_concurrency, true}] ++
|
||||
guild_ets_utils:heir_options().
|
||||
|
||||
-spec ensure_identify_table() -> ok.
|
||||
ensure_identify_table() ->
|
||||
case ets:whereis(?IDENTIFY_TABLE) of
|
||||
@@ -313,6 +312,38 @@ prune_old_identify_entries_keeps_recent_buckets_test() ->
|
||||
?assertNotEqual([], ets:lookup(?IDENTIFY_TABLE, RecentKey)),
|
||||
ets:delete(?IDENTIFY_TABLE, RecentKey).
|
||||
|
||||
identify_bucket_survives_creating_process_death_test() ->
|
||||
drop_identify_table(),
|
||||
{ok, _} = guild_ets_owner:start_link(),
|
||||
try
|
||||
assert_identify_bucket_outlives_creator()
|
||||
after
|
||||
gen_server:stop(guild_ets_owner)
|
||||
end.
|
||||
|
||||
assert_identify_bucket_outlives_creator() ->
|
||||
IP = <<"192.0.2.220">>,
|
||||
stop_table_owner(start_identify_bucket_creator(IP)),
|
||||
?assertNotEqual(undefined, ets:whereis(?IDENTIFY_TABLE)),
|
||||
Key = {IP, erlang:system_time(second) div ?IDENTIFY_WINDOW_SECS},
|
||||
?assertEqual([{Key, 1}], ets:lookup(?IDENTIFY_TABLE, Key)).
|
||||
|
||||
start_identify_bucket_creator(IP) ->
|
||||
Parent = self(),
|
||||
Pid = spawn(fun() -> create_identify_bucket(Parent, IP) end),
|
||||
receive
|
||||
{owner_ready, Pid} -> Pid
|
||||
after 1000 -> error(owner_start_timeout)
|
||||
end.
|
||||
|
||||
create_identify_bucket(Parent, IP) ->
|
||||
ok = check_identify_rate(IP),
|
||||
Parent ! {owner_ready, self()},
|
||||
receive
|
||||
stop -> ok
|
||||
after 30000 -> ok
|
||||
end.
|
||||
|
||||
identify_cleanup_loops_do_not_outlive_their_table_test() ->
|
||||
meck:new(gateway_retry_timer, [passthrough]),
|
||||
meck:expect(gateway_retry_timer, wait, fun(_) -> timer:sleep(5) end),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
-module(guild_ets_utils).
|
||||
-typing([eqwalizer]).
|
||||
|
||||
-export([ensure_table/2]).
|
||||
-export([ensure_table/2, heir_options/0]).
|
||||
|
||||
-spec ensure_table(atom(), list()) -> ok.
|
||||
ensure_table(TableName, Options) ->
|
||||
@@ -14,6 +14,13 @@ ensure_table(TableName, Options) ->
|
||||
ensure_table_local(TableName, Options)
|
||||
end.
|
||||
|
||||
-spec heir_options() -> list().
|
||||
heir_options() ->
|
||||
case whereis(guild_ets_owner) of
|
||||
Pid when is_pid(Pid), Pid =/= self() -> [{heir, Pid, inherited}];
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
-spec ensure_table_local(atom(), list()) -> ok.
|
||||
ensure_table_local(TableName, Options) ->
|
||||
case ets:whereis(TableName) of
|
||||
|
||||
Reference in New Issue
Block a user