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

Commit

Permalink
Use the ignored_users table to test event visibility & sync. (#12225)
Browse files Browse the repository at this point in the history
Instead of fetching the raw account data and re-parsing it. The
ignored_users table is a denormalised version of the account data
for quick searching.
  • Loading branch information
clokep authored Mar 15, 2022
1 parent dea5779 commit dda9b7f
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 47 deletions.
1 change: 1 addition & 0 deletions changelog.d/12225.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use the `ignored_users` table in additional places instead of re-parsing the account data.
30 changes: 2 additions & 28 deletions synapse/handlers/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import attr
from prometheus_client import Counter

from synapse.api.constants import AccountDataTypes, EventTypes, Membership, ReceiptTypes
from synapse.api.constants import EventTypes, Membership, ReceiptTypes
from synapse.api.filtering import FilterCollection
from synapse.api.presence import UserPresenceState
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
Expand Down Expand Up @@ -1601,7 +1601,7 @@ async def _generate_sync_entry_for_rooms(
return set(), set(), set(), set()

# 3. Work out which rooms need reporting in the sync response.
ignored_users = await self._get_ignored_users(user_id)
ignored_users = await self.store.ignored_users(user_id)
if since_token:
room_changes = await self._get_rooms_changed(
sync_result_builder, ignored_users
Expand All @@ -1627,7 +1627,6 @@ async def handle_room_entries(room_entry: "RoomSyncResultBuilder") -> None:
logger.debug("Generating room entry for %s", room_entry.room_id)
await self._generate_room_entry(
sync_result_builder,
ignored_users,
room_entry,
ephemeral=ephemeral_by_room.get(room_entry.room_id, []),
tags=tags_by_room.get(room_entry.room_id),
Expand Down Expand Up @@ -1657,29 +1656,6 @@ async def handle_room_entries(room_entry: "RoomSyncResultBuilder") -> None:
newly_left_users,
)

async def _get_ignored_users(self, user_id: str) -> FrozenSet[str]:
"""Retrieve the users ignored by the given user from their global account_data.
Returns an empty set if
- there is no global account_data entry for ignored_users
- there is such an entry, but it's not a JSON object.
"""
# TODO: Can we `SELECT ignored_user_id FROM ignored_users WHERE ignorer_user_id=?;` instead?
ignored_account_data = (
await self.store.get_global_account_data_by_type_for_user(
user_id=user_id, data_type=AccountDataTypes.IGNORED_USER_LIST
)
)

# If there is ignored users account data and it matches the proper type,
# then use it.
ignored_users: FrozenSet[str] = frozenset()
if ignored_account_data:
ignored_users_data = ignored_account_data.get("ignored_users", {})
if isinstance(ignored_users_data, dict):
ignored_users = frozenset(ignored_users_data.keys())
return ignored_users

async def _have_rooms_changed(
self, sync_result_builder: "SyncResultBuilder"
) -> bool:
Expand Down Expand Up @@ -2022,7 +1998,6 @@ async def _get_all_rooms(
async def _generate_room_entry(
self,
sync_result_builder: "SyncResultBuilder",
ignored_users: FrozenSet[str],
room_builder: "RoomSyncResultBuilder",
ephemeral: List[JsonDict],
tags: Optional[Dict[str, Dict[str, Any]]],
Expand Down Expand Up @@ -2051,7 +2026,6 @@ async def _generate_room_entry(
Args:
sync_result_builder
ignored_users: Set of users ignored by user.
room_builder
ephemeral: List of new ephemeral events for room
tags: List of *all* tags for room, or None if there has been
Expand Down
2 changes: 1 addition & 1 deletion synapse/push/bulk_push_rule_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ async def action_for_event_by_user(
if not event.is_state():
ignorers = await self.store.ignored_by(event.sender)
else:
ignorers = set()
ignorers = frozenset()

for uid, rules in rules_by_user.items():
if event.sender == uid:
Expand Down
41 changes: 38 additions & 3 deletions synapse/storage/databases/main/account_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@
# limitations under the License.

import logging
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Set, Tuple, cast
from typing import (
TYPE_CHECKING,
Any,
Dict,
FrozenSet,
Iterable,
List,
Optional,
Tuple,
cast,
)

from synapse.api.constants import AccountDataTypes
from synapse.replication.slave.storage._slaved_id_tracker import SlavedIdTracker
Expand Down Expand Up @@ -365,7 +375,7 @@ def get_updated_account_data_for_user_txn(
)

@cached(max_entries=5000, iterable=True)
async def ignored_by(self, user_id: str) -> Set[str]:
async def ignored_by(self, user_id: str) -> FrozenSet[str]:
"""
Get users which ignore the given user.
Expand All @@ -375,7 +385,7 @@ async def ignored_by(self, user_id: str) -> Set[str]:
Return:
The user IDs which ignore the given user.
"""
return set(
return frozenset(
await self.db_pool.simple_select_onecol(
table="ignored_users",
keyvalues={"ignored_user_id": user_id},
Expand All @@ -384,6 +394,26 @@ async def ignored_by(self, user_id: str) -> Set[str]:
)
)

@cached(max_entries=5000, iterable=True)
async def ignored_users(self, user_id: str) -> FrozenSet[str]:
"""
Get users which the given user ignores.
Params:
user_id: The user ID which is making the request.
Return:
The user IDs which are ignored by the given user.
"""
return frozenset(
await self.db_pool.simple_select_onecol(
table="ignored_users",
keyvalues={"ignorer_user_id": user_id},
retcol="ignored_user_id",
desc="ignored_users",
)
)

def process_replication_rows(
self,
stream_name: str,
Expand Down Expand Up @@ -529,6 +559,10 @@ def _add_account_data_for_user(
else:
currently_ignored_users = set()

# If the data has not changed, nothing to do.
if previously_ignored_users == currently_ignored_users:
return

# Delete entries which are no longer ignored.
self.db_pool.simple_delete_many_txn(
txn,
Expand All @@ -551,6 +585,7 @@ def _add_account_data_for_user(
# Invalidate the cache for any ignored users which were added or removed.
for ignored_user_id in previously_ignored_users ^ currently_ignored_users:
self._invalidate_cache_and_stream(txn, self.ignored_by, (ignored_user_id,))
self._invalidate_cache_and_stream(txn, self.ignored_users, (user_id,))

async def purge_account_data_for_user(self, user_id: str) -> None:
"""
Expand Down
18 changes: 3 additions & 15 deletions synapse/visibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@
import logging
from typing import Dict, FrozenSet, List, Optional

from synapse.api.constants import (
AccountDataTypes,
EventTypes,
HistoryVisibility,
Membership,
)
from synapse.api.constants import EventTypes, HistoryVisibility, Membership
from synapse.events import EventBase
from synapse.events.utils import prune_event
from synapse.storage import Storage
Expand Down Expand Up @@ -87,15 +82,8 @@ async def filter_events_for_client(
state_filter=StateFilter.from_types(types),
)

ignore_dict_content = await storage.main.get_global_account_data_by_type_for_user(
user_id, AccountDataTypes.IGNORED_USER_LIST
)

ignore_list: FrozenSet[str] = frozenset()
if ignore_dict_content:
ignored_users_dict = ignore_dict_content.get("ignored_users", {})
if isinstance(ignored_users_dict, dict):
ignore_list = frozenset(ignored_users_dict.keys())
# Get the users who are ignored by the requesting user.
ignore_list = await storage.main.ignored_users(user_id)

erased_senders = await storage.main.are_users_erased(e.sender for e in events)

Expand Down
17 changes: 17 additions & 0 deletions tests/storage/test_account_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,18 @@ def assert_ignorers(
expected_ignorer_user_ids,
)

def assert_ignored(
self, ignorer_user_id: str, expected_ignored_user_ids: Set[str]
) -> None:
self.assertEqual(
self.get_success(self.store.ignored_users(ignorer_user_id)),
expected_ignored_user_ids,
)

def test_ignoring_users(self):
"""Basic adding/removing of users from the ignore list."""
self._update_ignore_list("@other:test", "@another:remote")
self.assert_ignored(self.user, {"@other:test", "@another:remote"})

# Check a user which no one ignores.
self.assert_ignorers("@user:test", set())
Expand All @@ -62,6 +71,7 @@ def test_ignoring_users(self):

# Add one user, remove one user, and leave one user.
self._update_ignore_list("@foo:test", "@another:remote")
self.assert_ignored(self.user, {"@foo:test", "@another:remote"})

# Check the removed user.
self.assert_ignorers("@other:test", set())
Expand All @@ -76,20 +86,24 @@ def test_caching(self):
"""Ensure that caching works properly between different users."""
# The first user ignores a user.
self._update_ignore_list("@other:test")
self.assert_ignored(self.user, {"@other:test"})
self.assert_ignorers("@other:test", {self.user})

# The second user ignores them.
self._update_ignore_list("@other:test", ignorer_user_id="@second:test")
self.assert_ignored("@second:test", {"@other:test"})
self.assert_ignorers("@other:test", {self.user, "@second:test"})

# The first user un-ignores them.
self._update_ignore_list()
self.assert_ignored(self.user, set())
self.assert_ignorers("@other:test", {"@second:test"})

def test_invalid_data(self):
"""Invalid data ends up clearing out the ignored users list."""
# Add some data and ensure it is there.
self._update_ignore_list("@other:test")
self.assert_ignored(self.user, {"@other:test"})
self.assert_ignorers("@other:test", {self.user})

# No ignored_users key.
Expand All @@ -102,10 +116,12 @@ def test_invalid_data(self):
)

# No one ignores the user now.
self.assert_ignored(self.user, set())
self.assert_ignorers("@other:test", set())

# Add some data and ensure it is there.
self._update_ignore_list("@other:test")
self.assert_ignored(self.user, {"@other:test"})
self.assert_ignorers("@other:test", {self.user})

# Invalid data.
Expand All @@ -118,4 +134,5 @@ def test_invalid_data(self):
)

# No one ignores the user now.
self.assert_ignored(self.user, set())
self.assert_ignorers("@other:test", set())

0 comments on commit dda9b7f

Please sign in to comment.