Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 39bed28

Browse files
authored
SpamChecker metrics (#12513)
* add Measure blocks all over SpamChecker Signed-off-by: jesopo <github@lolnerd.net> * fix test_spam_checker_may_join_room and test_threepid_invite_spamcheck * better changelog entry
1 parent c9fc2c0 commit 39bed28

File tree

4 files changed

+64
-26
lines changed

4 files changed

+64
-26
lines changed

changelog.d/12513.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Measure the time taken in spam-checking callbacks and expose those measurements as metrics.

synapse/events/spamcheck.py

+58-23
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from synapse.spam_checker_api import RegistrationBehaviour
3333
from synapse.types import RoomAlias, UserProfile
3434
from synapse.util.async_helpers import delay_cancellation, maybe_awaitable
35+
from synapse.util.metrics import Measure
3536

3637
if TYPE_CHECKING:
3738
import synapse.events
@@ -162,7 +163,10 @@ def run(*args: Any, **kwargs: Any) -> Awaitable:
162163

163164

164165
class SpamChecker:
165-
def __init__(self) -> None:
166+
def __init__(self, hs: "synapse.server.HomeServer") -> None:
167+
self.hs = hs
168+
self.clock = hs.get_clock()
169+
166170
self._check_event_for_spam_callbacks: List[CHECK_EVENT_FOR_SPAM_CALLBACK] = []
167171
self._user_may_join_room_callbacks: List[USER_MAY_JOIN_ROOM_CALLBACK] = []
168172
self._user_may_invite_callbacks: List[USER_MAY_INVITE_CALLBACK] = []
@@ -255,7 +259,10 @@ async def check_event_for_spam(
255259
will be used as the error message returned to the user.
256260
"""
257261
for callback in self._check_event_for_spam_callbacks:
258-
res: Union[bool, str] = await delay_cancellation(callback(event))
262+
with Measure(
263+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
264+
):
265+
res: Union[bool, str] = await delay_cancellation(callback(event))
259266
if res:
260267
return res
261268

@@ -276,9 +283,12 @@ async def user_may_join_room(
276283
Whether the user may join the room
277284
"""
278285
for callback in self._user_may_join_room_callbacks:
279-
may_join_room = await delay_cancellation(
280-
callback(user_id, room_id, is_invited)
281-
)
286+
with Measure(
287+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
288+
):
289+
may_join_room = await delay_cancellation(
290+
callback(user_id, room_id, is_invited)
291+
)
282292
if may_join_room is False:
283293
return False
284294

@@ -300,9 +310,12 @@ async def user_may_invite(
300310
True if the user may send an invite, otherwise False
301311
"""
302312
for callback in self._user_may_invite_callbacks:
303-
may_invite = await delay_cancellation(
304-
callback(inviter_userid, invitee_userid, room_id)
305-
)
313+
with Measure(
314+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
315+
):
316+
may_invite = await delay_cancellation(
317+
callback(inviter_userid, invitee_userid, room_id)
318+
)
306319
if may_invite is False:
307320
return False
308321

@@ -328,9 +341,12 @@ async def user_may_send_3pid_invite(
328341
True if the user may send the invite, otherwise False
329342
"""
330343
for callback in self._user_may_send_3pid_invite_callbacks:
331-
may_send_3pid_invite = await delay_cancellation(
332-
callback(inviter_userid, medium, address, room_id)
333-
)
344+
with Measure(
345+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
346+
):
347+
may_send_3pid_invite = await delay_cancellation(
348+
callback(inviter_userid, medium, address, room_id)
349+
)
334350
if may_send_3pid_invite is False:
335351
return False
336352

@@ -348,7 +364,10 @@ async def user_may_create_room(self, userid: str) -> bool:
348364
True if the user may create a room, otherwise False
349365
"""
350366
for callback in self._user_may_create_room_callbacks:
351-
may_create_room = await delay_cancellation(callback(userid))
367+
with Measure(
368+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
369+
):
370+
may_create_room = await delay_cancellation(callback(userid))
352371
if may_create_room is False:
353372
return False
354373

@@ -369,9 +388,12 @@ async def user_may_create_room_alias(
369388
True if the user may create a room alias, otherwise False
370389
"""
371390
for callback in self._user_may_create_room_alias_callbacks:
372-
may_create_room_alias = await delay_cancellation(
373-
callback(userid, room_alias)
374-
)
391+
with Measure(
392+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
393+
):
394+
may_create_room_alias = await delay_cancellation(
395+
callback(userid, room_alias)
396+
)
375397
if may_create_room_alias is False:
376398
return False
377399

@@ -390,7 +412,10 @@ async def user_may_publish_room(self, userid: str, room_id: str) -> bool:
390412
True if the user may publish the room, otherwise False
391413
"""
392414
for callback in self._user_may_publish_room_callbacks:
393-
may_publish_room = await delay_cancellation(callback(userid, room_id))
415+
with Measure(
416+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
417+
):
418+
may_publish_room = await delay_cancellation(callback(userid, room_id))
394419
if may_publish_room is False:
395420
return False
396421

@@ -412,9 +437,13 @@ async def check_username_for_spam(self, user_profile: UserProfile) -> bool:
412437
True if the user is spammy.
413438
"""
414439
for callback in self._check_username_for_spam_callbacks:
415-
# Make a copy of the user profile object to ensure the spam checker cannot
416-
# modify it.
417-
if await delay_cancellation(callback(user_profile.copy())):
440+
with Measure(
441+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
442+
):
443+
# Make a copy of the user profile object to ensure the spam checker cannot
444+
# modify it.
445+
res = await delay_cancellation(callback(user_profile.copy()))
446+
if res:
418447
return True
419448

420449
return False
@@ -442,9 +471,12 @@ async def check_registration_for_spam(
442471
"""
443472

444473
for callback in self._check_registration_for_spam_callbacks:
445-
behaviour = await delay_cancellation(
446-
callback(email_threepid, username, request_info, auth_provider_id)
447-
)
474+
with Measure(
475+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
476+
):
477+
behaviour = await delay_cancellation(
478+
callback(email_threepid, username, request_info, auth_provider_id)
479+
)
448480
assert isinstance(behaviour, RegistrationBehaviour)
449481
if behaviour != RegistrationBehaviour.ALLOW:
450482
return behaviour
@@ -486,7 +518,10 @@ async def check_media_file_for_spam(
486518
"""
487519

488520
for callback in self._check_media_file_for_spam_callbacks:
489-
spam = await delay_cancellation(callback(file_wrapper, file_info))
521+
with Measure(
522+
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
523+
):
524+
spam = await delay_cancellation(callback(file_wrapper, file_info))
490525
if spam:
491526
return True
492527

synapse/server.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ def get_stats_handler(self) -> StatsHandler:
681681

682682
@cache_in_self
683683
def get_spam_checker(self) -> SpamChecker:
684-
return SpamChecker()
684+
return SpamChecker(self)
685685

686686
@cache_in_self
687687
def get_third_party_event_rules(self) -> ThirdPartyEventRules:

tests/rest/client/test_rooms.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ async def user_may_join_room(
925925
) -> bool:
926926
return return_value
927927

928-
callback_mock = Mock(side_effect=user_may_join_room)
928+
callback_mock = Mock(side_effect=user_may_join_room, spec=lambda *x: None)
929929
self.hs.get_spam_checker()._user_may_join_room_callbacks.append(callback_mock)
930930

931931
# Join a first room, without being invited to it.
@@ -2856,7 +2856,9 @@ def test_threepid_invite_spamcheck(self) -> None:
28562856

28572857
# Add a mock to the spamchecker callbacks for user_may_send_3pid_invite. Make it
28582858
# allow everything for now.
2859-
mock = Mock(return_value=make_awaitable(True))
2859+
# `spec` argument is needed for this function mock to have `__qualname__`, which
2860+
# is needed for `Measure` metrics buried in SpamChecker.
2861+
mock = Mock(return_value=make_awaitable(True), spec=lambda *x: None)
28602862
self.hs.get_spam_checker()._user_may_send_3pid_invite_callbacks.append(mock)
28612863

28622864
# Send a 3PID invite into the room and check that it succeeded.

0 commit comments

Comments
 (0)