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

Reduce the amount of state we pull from the DB #12811

Merged
merged 14 commits into from
Jun 6, 2022
Merged
1 change: 1 addition & 0 deletions changelog.d/12811.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reduce the amount of state we pull from the DB.
36 changes: 17 additions & 19 deletions synapse/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ def __init__(self, hs: "HomeServer"):
self.hs = hs
self.clock = hs.get_clock()
self.store = hs.get_datastores().main
self.state = hs.get_state_handler()
self._account_validity_handler = hs.get_account_validity_handler()

self.token_cache: LruCache[str, Tuple[str, bool]] = LruCache(
Expand All @@ -81,7 +80,7 @@ async def check_user_in_room(
user_id: str,
current_state: Optional[StateMap[EventBase]] = None,
allow_departed_users: bool = False,
) -> EventBase:
) -> Tuple[str, Optional[str]]:
"""Check if the user is in the room, or was at some point.
Args:
room_id: The room to check.
Expand All @@ -99,29 +98,28 @@ async def check_user_in_room(
Raises:
AuthError if the user is/was not in the room.
Returns:
Membership event for the user if the user was in the
room. This will be the join event if they are currently joined to
the room. This will be the leave event if they have left the room.
The current membership of the user in the room and the
membership event ID of the user.
"""
if current_state:
member = current_state.get((EventTypes.Member, user_id), None)
else:
member = await self.state.get_current_state(
room_id=room_id, event_type=EventTypes.Member, state_key=user_id
)

if member:
membership = member.membership
(
membership,
member_event_id,
) = await self.store.get_local_current_membership_for_user_in_room(
user_id=user_id,
room_id=room_id,
)
erikjohnston marked this conversation as resolved.
Show resolved Hide resolved

if membership:
if membership == Membership.JOIN:
return member
return membership, member_event_id

# XXX this looks totally bogus. Why do we not allow users who have been banned,
# or those who were members previously and have been re-invited?
if allow_departed_users and membership == Membership.LEAVE:
forgot = await self.store.did_forget(user_id, room_id)
if not forgot:
return member
return membership, member_event_id

raise AuthError(403, "User %s not in room %s" % (user_id, room_id))

Expand Down Expand Up @@ -602,7 +600,8 @@ async def check_can_change_room_list(self, room_id: str, user: UserID) -> bool:
# We currently require the user is a "moderator" in the room. We do this
# by checking if they would (theoretically) be able to change the
# m.room.canonical_alias events
power_level_event = await self.state.get_current_state(

power_level_event = await self.store.get_current_state_event(
room_id, EventTypes.PowerLevels, ""
)

Expand Down Expand Up @@ -693,12 +692,11 @@ async def check_user_in_room_or_world_readable(
# * The user is a non-guest user, and was ever in the room
# * The user is a guest user, and has joined the room
# else it will throw.
member_event = await self.check_user_in_room(
return await self.check_user_in_room(
room_id, user_id, allow_departed_users=allow_departed_users
)
return member_event.membership, member_event.event_id
except AuthError:
visibility = await self.state.get_current_state(
visibility = await self.store.get_current_state_event(
room_id, EventTypes.RoomHistoryVisibility, ""
)
if (
Expand Down
12 changes: 4 additions & 8 deletions synapse/federation/federation_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1167,14 +1167,10 @@ async def check_server_matches_acl(self, server_name: str, room_id: str) -> None
Raises:
AuthError if the server does not match the ACL
"""
state_ids = await self.store.get_current_state_ids(room_id)
acl_event_id = state_ids.get((EventTypes.ServerACL, ""))

if not acl_event_id:
return

acl_event = await self.store.get_event(acl_event_id)
if server_matches_acl_event(server_name, acl_event):
acl_event = await self.store.get_current_state_event(
room_id, EventTypes.ServerACL, ""
)
if not acl_event or server_matches_acl_event(server_name, acl_event):
return

raise AuthError(code=403, msg="Server is banned from room")
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ async def _update_canonical_alias(
Raises:
ShadowBanError if the requester has been shadow-banned.
"""
alias_event = await self.state.get_current_state(
alias_event = await self.store.get_current_state_event(
room_id, EventTypes.CanonicalAlias, ""
)

Expand Down
23 changes: 14 additions & 9 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@
import logging
from enum import Enum
from http import HTTPStatus
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Tuple, Union
from typing import (
TYPE_CHECKING,
Collection,
Dict,
Iterable,
List,
Optional,
Tuple,
Union,
)

import attr
from signedjson.key import decode_verify_key_bytes
Expand Down Expand Up @@ -353,15 +362,11 @@ async def _maybe_backfill_inner(
# First we try hosts that are already in the room
# TODO: HEURISTIC ALERT.

curr_state = await self.state_handler.get_current_state(room_id)

curr_domains = get_domains_from_state(curr_state)

likely_domains = [
domain for domain, depth in curr_domains if domain != self.server_name
]
users_in_room = await self.store.get_users_in_room(room_id)
likely_domains = {get_domain_from_id(u) for u in users_in_room}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, get_domains_from_state was doing more here and ordering the servers by oldest user first.

likely_domains.discard(self.server_name)

async def try_backfill(domains: List[str]) -> bool:
async def try_backfill(domains: Collection[str]) -> bool:
# TODO: Should we try multiple of these at a time?
for dom in domains:
try:
Expand Down
14 changes: 8 additions & 6 deletions synapse/handlers/federation_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -1558,9 +1558,9 @@ async def _maybe_kick_guest_users(self, event: EventBase) -> None:
if guest_access == GuestAccess.CAN_JOIN:
return

current_state_map = await self._state_handler.get_current_state(event.room_id)
current_state = list(current_state_map.values())
await self._get_room_member_handler().kick_guest_users(current_state)
current_state = await self._store.get_current_state(event.room_id)
current_state_list = list(current_state.values())
await self._get_room_member_handler().kick_guest_users(current_state_list)

async def _check_for_soft_fail(
self,
Expand Down Expand Up @@ -1588,6 +1588,9 @@ async def _check_for_soft_fail(
room_version = await self._store.get_room_version_id(event.room_id)
room_version_obj = KNOWN_ROOM_VERSIONS[room_version]

# The event types we want to pull from the "current" state.
auth_types = auth_types_for_event(room_version_obj, event)

# Calculate the "current state".
if state is not None:
# If we're explicitly given the state then we won't have all the
Expand All @@ -1614,8 +1617,8 @@ async def _check_for_soft_fail(
k: e.event_id for k, e in current_states.items()
}
else:
current_state_ids = await self._state_handler.get_current_state_ids(
event.room_id, latest_event_ids=extrem_ids
current_state_ids = await self._store.get_filtered_current_state_ids(
event.room_id, StateFilter.from_types(auth_types)
)

logger.debug(
Expand All @@ -1625,7 +1628,6 @@ async def _check_for_soft_fail(
)

# Now check if event pass auth against said current state
auth_types = auth_types_for_event(room_version_obj, event)
current_state_ids_list = [
e for k, e in current_state_ids.items() if k in auth_types
]
Expand Down
4 changes: 2 additions & 2 deletions synapse/handlers/initial_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ async def handle_room(event: RoomsForUser) -> None:
if event.membership == Membership.JOIN:
room_end_token = now_token.room_key
deferred_room_state = run_in_background(
self.state_handler.get_current_state, event.room_id
self.store.get_current_state, event.room_id
)
elif event.membership == Membership.LEAVE:
room_end_token = RoomStreamToken(
Expand Down Expand Up @@ -404,7 +404,7 @@ async def _room_initial_sync_joined(
membership: str,
is_peeking: bool,
) -> JsonDict:
current_state = await self.state.get_current_state(room_id=room_id)
current_state = await self.store.get_current_state(room_id=room_id)

# TODO: These concurrently
time_now = self.clock.time_msec()
Expand Down
4 changes: 3 additions & 1 deletion synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ async def get_room_data(
)

if membership == Membership.JOIN:
data = await self.state.get_current_state(room_id, event_type, state_key)
data = await self.store.get_current_state_event(
room_id, event_type, state_key
)
elif membership == Membership.LEAVE:
key = (event_type, state_key)
# If the membership is not JOIN, then the event ID should exist.
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -1399,7 +1399,7 @@ async def get_event_for_timestamp(
)

# Find other homeservers from the given state in the room
curr_state = await self.state_handler.get_current_state(room_id)
curr_state = await self.store.get_current_state(room_id)
curr_domains = get_domains_from_state(curr_state)
likely_domains = [
domain for domain, depth in curr_domains if domain != self.server_name
Expand Down
16 changes: 14 additions & 2 deletions synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,19 @@ async def _make_and_store_3pid_invite(
txn_id: Optional[str],
id_access_token: Optional[str] = None,
) -> int:
room_state = await self.state_handler.get_current_state(room_id)
room_state = await self.store.get_filtered_current_state(
room_id,
StateFilter.from_types(
[
(EventTypes.Member, user.to_string()),
(EventTypes.CanonicalAlias, ""),
(EventTypes.Name, ""),
(EventTypes.Create, ""),
(EventTypes.JoinRules, ""),
(EventTypes.RoomAvatar, ""),
]
),
)

inviter_display_name = ""
inviter_avatar_url = ""
Expand Down Expand Up @@ -1805,7 +1817,7 @@ async def _user_left_room(self, target: UserID, room_id: str) -> None:
async def forget(self, user: UserID, room_id: str) -> None:
user_id = user.to_string()

member = await self.state_handler.get_current_state(
member = await self.store.get_current_state_event(
room_id=room_id, event_type=EventTypes.Member, state_key=user_id
)
membership = member.membership if member else None
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ async def _search(
state_results = {}
if include_state:
for room_id in {e.room_id for e in search_result.allowed_events}:
state = await self.state_handler.get_current_state(room_id)
state = await self.store.get_current_state(room_id)
state_results[room_id] = list(state.values())

aggregations = await self._relations_handler.get_bundled_aggregations(
Expand Down
2 changes: 1 addition & 1 deletion synapse/notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ async def _get_room_ids(
return joined_room_ids, True

async def _is_world_readable(self, room_id: str) -> bool:
state = await self.state_handler.get_current_state(
state = await self.store.get_current_state_event(
room_id, EventTypes.RoomHistoryVisibility, ""
)
if state and "history_visibility" in state.content:
Expand Down
30 changes: 21 additions & 9 deletions synapse/rest/admin/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
assert_user_is_admin,
)
from synapse.storage.databases.main.room import RoomSortOrder
from synapse.storage.state import StateFilter
from synapse.types import JsonDict, RoomID, UserID, create_requester
from synapse.util import json_decoder

Expand Down Expand Up @@ -447,7 +448,7 @@ def __init__(self, hs: "HomeServer"):
super().__init__(hs)
self.auth = hs.get_auth()
self.admin_handler = hs.get_admin_handler()
self.state_handler = hs.get_state_handler()
self.store = hs.get_datastores().main
self.is_mine = hs.is_mine

async def on_POST(
Expand Down Expand Up @@ -489,8 +490,9 @@ async def on_POST(
)

# send invite if room has "JoinRules.INVITE"
room_state = await self.state_handler.get_current_state(room_id)
join_rules_event = room_state.get((EventTypes.JoinRules, ""))
join_rules_event = await self.store.get_current_state_event(
room_id, EventTypes.JoinRules, ""
)
if join_rules_event:
if not (join_rules_event.content.get("join_rule") == JoinRules.PUBLIC):
# update_membership with an action of "invite" can raise a
Expand Down Expand Up @@ -552,12 +554,22 @@ async def on_POST(
user_to_add = content.get("user_id", requester.user.to_string())

# Figure out which local users currently have power in the room, if any.
room_state = await self.state_handler.get_current_state(room_id)
if not room_state:
filtered_room_state = await self.store.get_filtered_current_state(
room_id,
StateFilter.from_types(
[
(EventTypes.Create, ""),
(EventTypes.PowerLevels, ""),
(EventTypes.JoinRules, ""),
(EventTypes.Member, user_to_add),
]
),
)
if not filtered_room_state:
raise SynapseError(HTTPStatus.BAD_REQUEST, "Server not in room")

create_event = room_state[(EventTypes.Create, "")]
power_levels = room_state.get((EventTypes.PowerLevels, ""))
create_event = filtered_room_state[(EventTypes.Create, "")]
power_levels = filtered_room_state.get((EventTypes.PowerLevels, ""))

if power_levels is not None:
# We pick the local user with the highest power.
Expand Down Expand Up @@ -633,7 +645,7 @@ async def on_POST(

# Now we check if the user we're granting admin rights to is already in
# the room. If not and it's not a public room we invite them.
member_event = room_state.get((EventTypes.Member, user_to_add))
member_event = filtered_room_state.get((EventTypes.Member, user_to_add))
is_joined = False
if member_event:
is_joined = member_event.content["membership"] in (
Expand All @@ -644,7 +656,7 @@ async def on_POST(
if is_joined:
return HTTPStatus.OK, {}

join_rules = room_state.get((EventTypes.JoinRules, ""))
join_rules = filtered_room_state.get((EventTypes.JoinRules, ""))
is_public = False
if join_rules:
is_public = join_rules.content.get("join_rule") == JoinRules.PUBLIC
Expand Down
2 changes: 1 addition & 1 deletion synapse/rest/client/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ async def on_GET(
if include_unredacted_content and not await self.auth.is_server_admin(
requester.user
):
power_level_event = await self._state.get_current_state(
power_level_event = await self._store.get_current_state_event(
room_id, EventTypes.PowerLevels, ""
)

Expand Down
4 changes: 2 additions & 2 deletions synapse/server_notices/resource_limits_server_notices.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ async def _is_room_currently_blocked(self, room_id: str) -> Tuple[bool, List[str
currently_blocked = False
pinned_state_event = None
try:
pinned_state_event = await self._state.get_current_state(
room_id, event_type=EventTypes.Pinned
pinned_state_event = await self._store.get_current_state_event(
room_id, event_type=EventTypes.Pinned, state_key=""
)
except AuthError:
# The user has yet to join the server notices room
Expand Down
Loading