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

Commit

Permalink
Reduce the amount of state we pull from the DB (#12811)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikjohnston authored Jun 6, 2022
1 parent 6b46c3e commit e3163e2
Show file tree
Hide file tree
Showing 23 changed files with 161 additions and 146 deletions.
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.
45 changes: 22 additions & 23 deletions synapse/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@
MissingClientTokenError,
)
from synapse.appservice import ApplicationService
from synapse.events import EventBase
from synapse.http import get_request_user_agent
from synapse.http.site import SynapseRequest
from synapse.logging.opentracing import active_span, force_tracing, start_active_span
from synapse.storage.databases.main.registration import TokenLookupResult
from synapse.types import Requester, StateMap, UserID, create_requester
from synapse.types import Requester, UserID, create_requester
from synapse.util.caches.lrucache import LruCache
from synapse.util.macaroons import get_value_from_macaroon, satisfy_expiry

Expand All @@ -61,8 +60,8 @@ 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._storage_controllers = hs.get_storage_controllers()

self.token_cache: LruCache[str, Tuple[str, bool]] = LruCache(
10000, "token_cache"
Expand All @@ -79,9 +78,8 @@ async def check_user_in_room(
self,
room_id: str,
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 +97,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,
)

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,8 +599,11 @@ 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(
room_id, EventTypes.PowerLevels, ""

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

auth_events = {}
Expand Down Expand Up @@ -693,12 +693,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._storage_controllers.state.get_current_state_event(
room_id, EventTypes.RoomHistoryVisibility, ""
)
if (
Expand Down
1 change: 1 addition & 0 deletions synapse/federation/federation_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def __init__(self, hs: "HomeServer"):
self.spam_checker = hs.get_spam_checker()
self.store = hs.get_datastores().main
self._clock = hs.get_clock()
self._storage_controllers = hs.get_storage_controllers()

async def _check_sigs_and_hash(
self, room_version: RoomVersion, pdu: EventBase
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 @@ -1223,14 +1223,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._state_storage_controller.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._storage_controllers.state.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 @@ -320,7 +320,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._storage_controllers.state.get_current_state_event(
room_id, EventTypes.CanonicalAlias, ""
)

Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ 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_state = await self._storage_controllers.state.get_current_state(room_id)

curr_domains = get_domains_from_state(curr_state)

Expand Down
18 changes: 12 additions & 6 deletions synapse/handlers/federation_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -1584,9 +1584,11 @@ 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._storage_controllers.state.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 @@ -1614,6 +1616,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_ids is not None:
# If we're explicitly given the state then we won't have all the
Expand Down Expand Up @@ -1643,8 +1648,10 @@ async def _check_for_soft_fail(
)
)
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._state_storage_controller.get_current_state_ids(
event.room_id, StateFilter.from_types(auth_types)
)
)

logger.debug(
Expand All @@ -1654,7 +1661,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
6 changes: 4 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._state_storage_controller.get_current_state, event.room_id
)
elif event.membership == Membership.LEAVE:
room_end_token = RoomStreamToken(
Expand Down Expand Up @@ -407,7 +407,9 @@ 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._storage_controllers.state.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 @@ -125,7 +125,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._storage_controllers.state.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
5 changes: 4 additions & 1 deletion synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,7 @@ def __init__(self, hs: "HomeServer"):
self.store = hs.get_datastores().main
self.state_handler = hs.get_state_handler()
self.federation_client = hs.get_federation_client()
self._storage_controllers = hs.get_storage_controllers()

async def get_event_for_timestamp(
self,
Expand Down Expand Up @@ -1406,7 +1407,9 @@ 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._storage_controllers.state.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 @@ -1401,7 +1401,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._storage_controllers.state.get_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 @@ -1797,7 +1809,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._storage_controllers.state.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._storage_controllers.state.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._storage_controllers.state.get_current_state_event(
room_id, EventTypes.RoomHistoryVisibility, ""
)
if state and "history_visibility" in state.content:
Expand Down
34 changes: 25 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 @@ -448,7 +449,8 @@ 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._storage_controllers = hs.get_storage_controllers()
self.is_mine = hs.is_mine

async def on_POST(
Expand Down Expand Up @@ -490,8 +492,11 @@ 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._storage_controllers.state.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 @@ -536,6 +541,7 @@ def __init__(self, hs: "HomeServer"):
super().__init__(hs)
self.auth = hs.get_auth()
self.store = hs.get_datastores().main
self._state_storage_controller = hs.get_storage_controllers().state
self.event_creation_handler = hs.get_event_creation_handler()
self.state_handler = hs.get_state_handler()
self.is_mine_id = hs.is_mine_id
Expand All @@ -553,12 +559,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._state_storage_controller.get_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 @@ -634,7 +650,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 @@ -645,7 +661,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
Loading

0 comments on commit e3163e2

Please sign in to comment.