mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-05-03 00:44:49 -04:00
Add type hints to state database module. (#10823)
This commit is contained in:
parent
b93259082c
commit
3eba047d38
6 changed files with 133 additions and 72 deletions
|
@ -13,43 +13,56 @@
|
|||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
from typing import Dict, Iterable, List, Optional, Set, Tuple
|
||||
from typing import TYPE_CHECKING, Collection, Dict, Iterable, List, Optional, Set, Tuple
|
||||
|
||||
import attr
|
||||
|
||||
from synapse.api.constants import EventTypes
|
||||
from synapse.storage._base import SQLBaseStore
|
||||
from synapse.storage.database import DatabasePool
|
||||
from synapse.storage.database import (
|
||||
DatabasePool,
|
||||
LoggingDatabaseConnection,
|
||||
LoggingTransaction,
|
||||
)
|
||||
from synapse.storage.databases.state.bg_updates import StateBackgroundUpdateStore
|
||||
from synapse.storage.state import StateFilter
|
||||
from synapse.storage.types import Cursor
|
||||
from synapse.storage.util.sequence import build_sequence_generator
|
||||
from synapse.types import MutableStateMap, StateMap
|
||||
from synapse.types import MutableStateMap, StateKey, StateMap
|
||||
from synapse.util.caches.descriptors import cached
|
||||
from synapse.util.caches.dictionary_cache import DictionaryCache
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from synapse.server import HomeServer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MAX_STATE_DELTA_HOPS = 100
|
||||
|
||||
|
||||
class _GetStateGroupDelta(
|
||||
namedtuple("_GetStateGroupDelta", ("prev_group", "delta_ids"))
|
||||
):
|
||||
@attr.s(slots=True, frozen=True, auto_attribs=True)
|
||||
class _GetStateGroupDelta:
|
||||
"""Return type of get_state_group_delta that implements __len__, which lets
|
||||
us use the itrable flag when caching
|
||||
us use the iterable flag when caching
|
||||
"""
|
||||
|
||||
__slots__ = []
|
||||
prev_group: Optional[int]
|
||||
delta_ids: Optional[StateMap[str]]
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self) -> int:
|
||||
return len(self.delta_ids) if self.delta_ids else 0
|
||||
|
||||
|
||||
class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
||||
"""A data store for fetching/storing state groups."""
|
||||
|
||||
def __init__(self, database: DatabasePool, db_conn, hs):
|
||||
def __init__(
|
||||
self,
|
||||
database: DatabasePool,
|
||||
db_conn: LoggingDatabaseConnection,
|
||||
hs: "HomeServer",
|
||||
):
|
||||
super().__init__(database, db_conn, hs)
|
||||
|
||||
# Originally the state store used a single DictionaryCache to cache the
|
||||
|
@ -81,19 +94,21 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
# We size the non-members cache to be smaller than the members cache as the
|
||||
# vast majority of state in Matrix (today) is member events.
|
||||
|
||||
self._state_group_cache = DictionaryCache(
|
||||
self._state_group_cache: DictionaryCache[int, StateKey, str] = DictionaryCache(
|
||||
"*stateGroupCache*",
|
||||
# TODO: this hasn't been tuned yet
|
||||
50000,
|
||||
)
|
||||
self._state_group_members_cache = DictionaryCache(
|
||||
self._state_group_members_cache: DictionaryCache[
|
||||
int, StateKey, str
|
||||
] = DictionaryCache(
|
||||
"*stateGroupMembersCache*",
|
||||
500000,
|
||||
)
|
||||
|
||||
def get_max_state_group_txn(txn: Cursor):
|
||||
def get_max_state_group_txn(txn: Cursor) -> int:
|
||||
txn.execute("SELECT COALESCE(max(id), 0) FROM state_groups")
|
||||
return txn.fetchone()[0]
|
||||
return txn.fetchone()[0] # type: ignore
|
||||
|
||||
self._state_group_seq_gen = build_sequence_generator(
|
||||
db_conn,
|
||||
|
@ -105,15 +120,15 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
)
|
||||
|
||||
@cached(max_entries=10000, iterable=True)
|
||||
async def get_state_group_delta(self, state_group):
|
||||
async def get_state_group_delta(self, state_group: int) -> _GetStateGroupDelta:
|
||||
"""Given a state group try to return a previous group and a delta between
|
||||
the old and the new.
|
||||
|
||||
Returns:
|
||||
(prev_group, delta_ids), where both may be None.
|
||||
_GetStateGroupDelta containing prev_group and delta_ids, where both may be None.
|
||||
"""
|
||||
|
||||
def _get_state_group_delta_txn(txn):
|
||||
def _get_state_group_delta_txn(txn: LoggingTransaction) -> _GetStateGroupDelta:
|
||||
prev_group = self.db_pool.simple_select_one_onecol_txn(
|
||||
txn,
|
||||
table="state_group_edges",
|
||||
|
@ -154,7 +169,7 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
Returns:
|
||||
Dict of state group to state map.
|
||||
"""
|
||||
results = {}
|
||||
results: Dict[int, StateMap[str]] = {}
|
||||
|
||||
chunks = [groups[i : i + 100] for i in range(0, len(groups), 100)]
|
||||
for chunk in chunks:
|
||||
|
@ -168,19 +183,24 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
|
||||
return results
|
||||
|
||||
def _get_state_for_group_using_cache(self, cache, group, state_filter):
|
||||
def _get_state_for_group_using_cache(
|
||||
self,
|
||||
cache: DictionaryCache[int, StateKey, str],
|
||||
group: int,
|
||||
state_filter: StateFilter,
|
||||
) -> Tuple[MutableStateMap[str], bool]:
|
||||
"""Checks if group is in cache. See `_get_state_for_groups`
|
||||
|
||||
Args:
|
||||
cache(DictionaryCache): the state group cache to use
|
||||
group(int): The state group to lookup
|
||||
state_filter (StateFilter): The state filter used to fetch state
|
||||
from the database.
|
||||
cache: the state group cache to use
|
||||
group: The state group to lookup
|
||||
state_filter: The state filter used to fetch state from the database.
|
||||
|
||||
Returns 2-tuple (`state_dict`, `got_all`).
|
||||
`got_all` is a bool indicating if we successfully retrieved all
|
||||
requests state from the cache, if False we need to query the DB for the
|
||||
missing state.
|
||||
Returns:
|
||||
2-tuple (`state_dict`, `got_all`).
|
||||
`got_all` is a bool indicating if we successfully retrieved all
|
||||
requests state from the cache, if False we need to query the DB for the
|
||||
missing state.
|
||||
"""
|
||||
cache_entry = cache.get(group)
|
||||
state_dict_ids = cache_entry.value
|
||||
|
@ -277,8 +297,11 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
return state
|
||||
|
||||
def _get_state_for_groups_using_cache(
|
||||
self, groups: Iterable[int], cache: DictionaryCache, state_filter: StateFilter
|
||||
) -> Tuple[Dict[int, StateMap[str]], Set[int]]:
|
||||
self,
|
||||
groups: Iterable[int],
|
||||
cache: DictionaryCache[int, StateKey, str],
|
||||
state_filter: StateFilter,
|
||||
) -> Tuple[Dict[int, MutableStateMap[str]], Set[int]]:
|
||||
"""Gets the state at each of a list of state groups, optionally
|
||||
filtering by type/state_key, querying from a specific cache.
|
||||
|
||||
|
@ -310,21 +333,21 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
|
||||
def _insert_into_cache(
|
||||
self,
|
||||
group_to_state_dict,
|
||||
state_filter,
|
||||
cache_seq_num_members,
|
||||
cache_seq_num_non_members,
|
||||
):
|
||||
group_to_state_dict: Dict[int, StateMap[str]],
|
||||
state_filter: StateFilter,
|
||||
cache_seq_num_members: int,
|
||||
cache_seq_num_non_members: int,
|
||||
) -> None:
|
||||
"""Inserts results from querying the database into the relevant cache.
|
||||
|
||||
Args:
|
||||
group_to_state_dict (dict): The new entries pulled from database.
|
||||
group_to_state_dict: The new entries pulled from database.
|
||||
Map from state group to state dict
|
||||
state_filter (StateFilter): The state filter used to fetch state
|
||||
state_filter: The state filter used to fetch state
|
||||
from the database.
|
||||
cache_seq_num_members (int): Sequence number of member cache since
|
||||
cache_seq_num_members: Sequence number of member cache since
|
||||
last lookup in cache
|
||||
cache_seq_num_non_members (int): Sequence number of member cache since
|
||||
cache_seq_num_non_members: Sequence number of member cache since
|
||||
last lookup in cache
|
||||
"""
|
||||
|
||||
|
@ -395,7 +418,7 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
The state group ID
|
||||
"""
|
||||
|
||||
def _store_state_group_txn(txn):
|
||||
def _store_state_group_txn(txn: LoggingTransaction) -> int:
|
||||
if current_state_ids is None:
|
||||
# AFAIK, this can never happen
|
||||
raise Exception("current_state_ids cannot be None")
|
||||
|
@ -426,6 +449,8 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
|
||||
potential_hops = self._count_state_group_hops_txn(txn, prev_group)
|
||||
if prev_group and potential_hops < MAX_STATE_DELTA_HOPS:
|
||||
assert delta_ids is not None
|
||||
|
||||
self.db_pool.simple_insert_txn(
|
||||
txn,
|
||||
table="state_group_edges",
|
||||
|
@ -498,7 +523,7 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
)
|
||||
|
||||
async def purge_unreferenced_state_groups(
|
||||
self, room_id: str, state_groups_to_delete
|
||||
self, room_id: str, state_groups_to_delete: Collection[int]
|
||||
) -> None:
|
||||
"""Deletes no longer referenced state groups and de-deltas any state
|
||||
groups that reference them.
|
||||
|
@ -506,8 +531,7 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
Args:
|
||||
room_id: The room the state groups belong to (must all be in the
|
||||
same room).
|
||||
state_groups_to_delete (Collection[int]): Set of all state groups
|
||||
to delete.
|
||||
state_groups_to_delete: Set of all state groups to delete.
|
||||
"""
|
||||
|
||||
await self.db_pool.runInteraction(
|
||||
|
@ -517,7 +541,12 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
state_groups_to_delete,
|
||||
)
|
||||
|
||||
def _purge_unreferenced_state_groups(self, txn, room_id, state_groups_to_delete):
|
||||
def _purge_unreferenced_state_groups(
|
||||
self,
|
||||
txn: LoggingTransaction,
|
||||
room_id: str,
|
||||
state_groups_to_delete: Collection[int],
|
||||
) -> None:
|
||||
logger.info(
|
||||
"[purge] found %i state groups to delete", len(state_groups_to_delete)
|
||||
)
|
||||
|
@ -546,8 +575,8 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
# groups to non delta versions.
|
||||
for sg in remaining_state_groups:
|
||||
logger.info("[purge] de-delta-ing remaining state group %s", sg)
|
||||
curr_state = self._get_state_groups_from_groups_txn(txn, [sg])
|
||||
curr_state = curr_state[sg]
|
||||
curr_state_by_group = self._get_state_groups_from_groups_txn(txn, [sg])
|
||||
curr_state = curr_state_by_group[sg]
|
||||
|
||||
self.db_pool.simple_delete_txn(
|
||||
txn, table="state_groups_state", keyvalues={"state_group": sg}
|
||||
|
@ -605,12 +634,14 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
|
||||
return {row["state_group"]: row["prev_state_group"] for row in rows}
|
||||
|
||||
async def purge_room_state(self, room_id, state_groups_to_delete):
|
||||
async def purge_room_state(
|
||||
self, room_id: str, state_groups_to_delete: Collection[int]
|
||||
) -> None:
|
||||
"""Deletes all record of a room from state tables
|
||||
|
||||
Args:
|
||||
room_id (str):
|
||||
state_groups_to_delete (list[int]): State groups to delete
|
||||
room_id:
|
||||
state_groups_to_delete: State groups to delete
|
||||
"""
|
||||
|
||||
await self.db_pool.runInteraction(
|
||||
|
@ -620,7 +651,12 @@ class StateGroupDataStore(StateBackgroundUpdateStore, SQLBaseStore):
|
|||
state_groups_to_delete,
|
||||
)
|
||||
|
||||
def _purge_room_state_txn(self, txn, room_id, state_groups_to_delete):
|
||||
def _purge_room_state_txn(
|
||||
self,
|
||||
txn: LoggingTransaction,
|
||||
room_id: str,
|
||||
state_groups_to_delete: Collection[int],
|
||||
) -> None:
|
||||
# first we have to delete the state groups states
|
||||
logger.info("[purge] removing %s from state_groups_state", room_id)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue