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

Use the ignored_users table when testing event visibility. #12225

Merged
merged 5 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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())