mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
perf(gateway): skip permission cache rebuild on no-op updates (#2187)
This commit is contained in:
@@ -531,24 +531,27 @@ handle_dispatch_call(#{event := Event, data := EventData}, State) ->
|
||||
|
||||
-spec dispatch_event(term(), term(), guild_state()) -> guild_state().
|
||||
dispatch_event(Event, EventData, State) ->
|
||||
ParsedEventData = parse_event_data(EventData),
|
||||
{noreply, NewState} = guild_dispatch:handle_dispatch(
|
||||
Event, parse_event_data(EventData), State
|
||||
Event, ParsedEventData, State
|
||||
),
|
||||
StateAfterPrune = guild_maintenance:maybe_prune_invalid_member_subscriptions(
|
||||
Event, NewState
|
||||
),
|
||||
ok = maybe_refresh_permission_cache(Event, StateAfterPrune),
|
||||
ok = maybe_refresh_permission_cache(Event, ParsedEventData, State, StateAfterPrune),
|
||||
StateAfterPrune.
|
||||
|
||||
-spec parse_event_data(term()) -> map().
|
||||
parse_event_data(D) when is_binary(D) -> require_map(json:decode(D));
|
||||
parse_event_data(D) when is_map(D) -> D.
|
||||
|
||||
-spec maybe_refresh_permission_cache(term(), guild_state()) -> ok.
|
||||
maybe_refresh_permission_cache(Event, State) ->
|
||||
-spec maybe_refresh_permission_cache(term(), map(), guild_state(), guild_state()) -> ok.
|
||||
maybe_refresh_permission_cache(Event, EventData, OldState, NewState) ->
|
||||
case event_mutates_guild_data(Event) of
|
||||
true -> guild_maintenance:maybe_put_permission_cache(State);
|
||||
false -> ok
|
||||
true ->
|
||||
guild_maintenance:maybe_put_permission_cache(Event, EventData, OldState, NewState);
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec event_mutates_guild_data(term()) -> boolean().
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
-export([
|
||||
update_counts/1,
|
||||
maybe_put_permission_cache/1,
|
||||
maybe_put_permission_cache/4,
|
||||
maybe_delete_permission_cache/2,
|
||||
maybe_put_guild_count_cache/3,
|
||||
schedule_count_cache_refresh/1,
|
||||
@@ -42,6 +43,29 @@ maybe_put_permission_cache(State) ->
|
||||
false -> guild_permission_cache:put_state(State)
|
||||
end.
|
||||
|
||||
-spec maybe_put_permission_cache(term(), map(), guild_state(), guild_state()) -> ok.
|
||||
maybe_put_permission_cache(Event, EventData, OldState, NewState) ->
|
||||
case permission_cache_needs_rebuild(Event, EventData, OldState, NewState) of
|
||||
true -> maybe_put_permission_cache(NewState);
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
-spec permission_cache_needs_rebuild(term(), map(), guild_state(), guild_state()) -> boolean().
|
||||
permission_cache_needs_rebuild(guild_member_update, EventData, OldState, NewState) ->
|
||||
guild_permission_cache:member_projection_changed(
|
||||
member_update_user_id(EventData),
|
||||
guild_data_index:ensure_data_map(OldState),
|
||||
guild_data_index:ensure_data_map(NewState)
|
||||
);
|
||||
permission_cache_needs_rebuild(_Event, _EventData, _OldState, _NewState) ->
|
||||
true.
|
||||
|
||||
-spec member_update_user_id(map()) -> user_id() | undefined.
|
||||
member_update_user_id(#{<<"user">> := #{<<"id">> := Id}}) ->
|
||||
snowflake_id:parse_maybe(Id);
|
||||
member_update_user_id(_) ->
|
||||
undefined.
|
||||
|
||||
-spec maybe_delete_permission_cache(term(), guild_state()) -> ok.
|
||||
maybe_delete_permission_cache(GuildId, State) ->
|
||||
case maps:get(disable_permission_cache_updates, State, false) of
|
||||
@@ -407,4 +431,84 @@ prune_channel(ChannelId, RoleId) ->
|
||||
]
|
||||
}.
|
||||
|
||||
permission_cache_skips_nick_only_member_update_test() ->
|
||||
UserId = 4242,
|
||||
OldState = permission_cache_state(
|
||||
7710001, permission_cache_member(UserId, [10], <<"old">>)
|
||||
),
|
||||
EventData = permission_cache_member(UserId, [10], <<"new">>),
|
||||
NewState = permission_cache_apply(EventData, OldState),
|
||||
?assertNotEqual(
|
||||
guild_data_index:get_member(UserId, maps:get(data, OldState)),
|
||||
guild_data_index:get_member(UserId, maps:get(data, NewState))
|
||||
),
|
||||
?assertEqual(
|
||||
{error, not_found},
|
||||
permission_cache_refresh(guild_member_update, EventData, OldState, NewState)
|
||||
).
|
||||
|
||||
permission_cache_rebuilds_on_member_role_change_test() ->
|
||||
UserId = 4242,
|
||||
OldState = permission_cache_state(
|
||||
7710002, permission_cache_member(UserId, [10], <<"old">>)
|
||||
),
|
||||
EventData = permission_cache_member(UserId, [10, 11], <<"old">>),
|
||||
NewState = permission_cache_apply(EventData, OldState),
|
||||
{ok, Snapshot} = permission_cache_refresh(
|
||||
guild_member_update, EventData, OldState, NewState
|
||||
),
|
||||
Member = guild_permissions:find_member_by_user_id(UserId, Snapshot),
|
||||
?assertEqual([10, 11], maps:get(<<"roles">>, Member)).
|
||||
|
||||
permission_cache_rebuilds_on_member_timeout_change_test() ->
|
||||
UserId = 4242,
|
||||
Until = <<"2026-09-01T00:00:00.000Z">>,
|
||||
OldState = permission_cache_state(
|
||||
7710003, permission_cache_member(UserId, [10], <<"old">>)
|
||||
),
|
||||
EventData = (permission_cache_member(UserId, [10], <<"old">>))#{
|
||||
<<"communication_disabled_until">> => Until
|
||||
},
|
||||
NewState = permission_cache_apply(EventData, OldState),
|
||||
{ok, Snapshot} = permission_cache_refresh(
|
||||
guild_member_update, EventData, OldState, NewState
|
||||
),
|
||||
Member = guild_permissions:find_member_by_user_id(UserId, Snapshot),
|
||||
?assertEqual(Until, maps:get(<<"communication_disabled_until">>, Member)).
|
||||
|
||||
permission_cache_rebuilds_for_other_mutating_events_test() ->
|
||||
State = permission_cache_state(7710004, permission_cache_member(4242, [10], <<"old">>)),
|
||||
?assertMatch({ok, _}, permission_cache_refresh(guild_role_update, #{}, State, State)).
|
||||
|
||||
permission_cache_rebuilds_without_member_update_user_id_test() ->
|
||||
State = permission_cache_state(7710005, permission_cache_member(4242, [10], <<"old">>)),
|
||||
?assertMatch({ok, _}, permission_cache_refresh(guild_member_update, #{}, State, State)).
|
||||
|
||||
permission_cache_refresh(Event, EventData, OldState, NewState) ->
|
||||
GuildId = maps:get(id, NewState),
|
||||
ok = guild_permission_cache:delete(GuildId),
|
||||
try
|
||||
ok = maybe_put_permission_cache(Event, EventData, OldState, NewState),
|
||||
guild_permission_cache:get_snapshot(GuildId)
|
||||
after
|
||||
ok = guild_permission_cache:delete(GuildId)
|
||||
end.
|
||||
|
||||
permission_cache_state(GuildId, Member) ->
|
||||
#{
|
||||
id => GuildId,
|
||||
data => guild_data_index:normalize_data(#{
|
||||
<<"guild">> => #{<<"owner_id">> => <<"999">>},
|
||||
<<"roles">> => [prune_role(10, 0), prune_role(11, 0)],
|
||||
<<"members">> => [Member],
|
||||
<<"channels">> => []
|
||||
})
|
||||
}.
|
||||
|
||||
permission_cache_member(UserId, RoleIds, Nick) ->
|
||||
(prune_member(UserId, RoleIds))#{<<"nick">> => Nick}.
|
||||
|
||||
permission_cache_apply(EventData, State) ->
|
||||
State#{data => guild_state_member:handle_member_update(EventData, maps:get(data, State))}.
|
||||
|
||||
-endif.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
has_member/2,
|
||||
get_member/2,
|
||||
strip_data/1,
|
||||
member_projection_changed/3,
|
||||
migrate_existing_entries/0
|
||||
]).
|
||||
|
||||
@@ -153,6 +154,13 @@ strip_data(Data) when is_map(Data) ->
|
||||
strip_data(Data) ->
|
||||
Data.
|
||||
|
||||
-spec member_projection_changed(user_id() | undefined, guild_data(), guild_data()) -> boolean().
|
||||
member_projection_changed(UserId, OldData, NewData) when is_integer(UserId) ->
|
||||
strip_member(guild_data_index:get_member(UserId, OldData)) =/=
|
||||
strip_member(guild_data_index:get_member(UserId, NewData));
|
||||
member_projection_changed(_UserId, _OldData, _NewData) ->
|
||||
true.
|
||||
|
||||
-spec strip_guild(map() | term()) -> map().
|
||||
strip_guild(Guild) when is_map(Guild) ->
|
||||
case snowflake_id:parse_optional(maps:get(<<"owner_id">>, Guild, undefined)) of
|
||||
|
||||
Reference in New Issue
Block a user