state.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import logging
  16. from six import iteritems, itervalues
  17. import attr
  18. from twisted.internet import defer
  19. from synapse.api.constants import EventTypes
  20. logger = logging.getLogger(__name__)
  21. @attr.s(slots=True)
  22. class StateFilter(object):
  23. """A filter used when querying for state.
  24. Attributes:
  25. types (dict[str, set[str]|None]): Map from type to set of state keys (or
  26. None). This specifies which state_keys for the given type to fetch
  27. from the DB. If None then all events with that type are fetched. If
  28. the set is empty then no events with that type are fetched.
  29. include_others (bool): Whether to fetch events with types that do not
  30. appear in `types`.
  31. """
  32. types = attr.ib()
  33. include_others = attr.ib(default=False)
  34. def __attrs_post_init__(self):
  35. # If `include_others` is set we canonicalise the filter by removing
  36. # wildcards from the types dictionary
  37. if self.include_others:
  38. self.types = {k: v for k, v in iteritems(self.types) if v is not None}
  39. @staticmethod
  40. def all():
  41. """Creates a filter that fetches everything.
  42. Returns:
  43. StateFilter
  44. """
  45. return StateFilter(types={}, include_others=True)
  46. @staticmethod
  47. def none():
  48. """Creates a filter that fetches nothing.
  49. Returns:
  50. StateFilter
  51. """
  52. return StateFilter(types={}, include_others=False)
  53. @staticmethod
  54. def from_types(types):
  55. """Creates a filter that only fetches the given types
  56. Args:
  57. types (Iterable[tuple[str, str|None]]): A list of type and state
  58. keys to fetch. A state_key of None fetches everything for
  59. that type
  60. Returns:
  61. StateFilter
  62. """
  63. type_dict = {}
  64. for typ, s in types:
  65. if typ in type_dict:
  66. if type_dict[typ] is None:
  67. continue
  68. if s is None:
  69. type_dict[typ] = None
  70. continue
  71. type_dict.setdefault(typ, set()).add(s)
  72. return StateFilter(types=type_dict)
  73. @staticmethod
  74. def from_lazy_load_member_list(members):
  75. """Creates a filter that returns all non-member events, plus the member
  76. events for the given users
  77. Args:
  78. members (iterable[str]): Set of user IDs
  79. Returns:
  80. StateFilter
  81. """
  82. return StateFilter(types={EventTypes.Member: set(members)}, include_others=True)
  83. def return_expanded(self):
  84. """Creates a new StateFilter where type wild cards have been removed
  85. (except for memberships). The returned filter is a superset of the
  86. current one, i.e. anything that passes the current filter will pass
  87. the returned filter.
  88. This helps the caching as the DictionaryCache knows if it has *all* the
  89. state, but does not know if it has all of the keys of a particular type,
  90. which makes wildcard lookups expensive unless we have a complete cache.
  91. Hence, if we are doing a wildcard lookup, populate the cache fully so
  92. that we can do an efficient lookup next time.
  93. Note that since we have two caches, one for membership events and one for
  94. other events, we can be a bit more clever than simply returning
  95. `StateFilter.all()` if `has_wildcards()` is True.
  96. We return a StateFilter where:
  97. 1. the list of membership events to return is the same
  98. 2. if there is a wildcard that matches non-member events we
  99. return all non-member events
  100. Returns:
  101. StateFilter
  102. """
  103. if self.is_full():
  104. # If we're going to return everything then there's nothing to do
  105. return self
  106. if not self.has_wildcards():
  107. # If there are no wild cards, there's nothing to do
  108. return self
  109. if EventTypes.Member in self.types:
  110. get_all_members = self.types[EventTypes.Member] is None
  111. else:
  112. get_all_members = self.include_others
  113. has_non_member_wildcard = self.include_others or any(
  114. state_keys is None
  115. for t, state_keys in iteritems(self.types)
  116. if t != EventTypes.Member
  117. )
  118. if not has_non_member_wildcard:
  119. # If there are no non-member wild cards we can just return ourselves
  120. return self
  121. if get_all_members:
  122. # We want to return everything.
  123. return StateFilter.all()
  124. else:
  125. # We want to return all non-members, but only particular
  126. # memberships
  127. return StateFilter(
  128. types={EventTypes.Member: self.types[EventTypes.Member]},
  129. include_others=True,
  130. )
  131. def make_sql_filter_clause(self):
  132. """Converts the filter to an SQL clause.
  133. For example:
  134. f = StateFilter.from_types([("m.room.create", "")])
  135. clause, args = f.make_sql_filter_clause()
  136. clause == "(type = ? AND state_key = ?)"
  137. args == ['m.room.create', '']
  138. Returns:
  139. tuple[str, list]: The SQL string (may be empty) and arguments. An
  140. empty SQL string is returned when the filter matches everything
  141. (i.e. is "full").
  142. """
  143. where_clause = ""
  144. where_args = []
  145. if self.is_full():
  146. return where_clause, where_args
  147. if not self.include_others and not self.types:
  148. # i.e. this is an empty filter, so we need to return a clause that
  149. # will match nothing
  150. return "1 = 2", []
  151. # First we build up a lost of clauses for each type/state_key combo
  152. clauses = []
  153. for etype, state_keys in iteritems(self.types):
  154. if state_keys is None:
  155. clauses.append("(type = ?)")
  156. where_args.append(etype)
  157. continue
  158. for state_key in state_keys:
  159. clauses.append("(type = ? AND state_key = ?)")
  160. where_args.extend((etype, state_key))
  161. # This will match anything that appears in `self.types`
  162. where_clause = " OR ".join(clauses)
  163. # If we want to include stuff that's not in the types dict then we add
  164. # a `OR type NOT IN (...)` clause to the end.
  165. if self.include_others:
  166. if where_clause:
  167. where_clause += " OR "
  168. where_clause += "type NOT IN (%s)" % (",".join(["?"] * len(self.types)),)
  169. where_args.extend(self.types)
  170. return where_clause, where_args
  171. def max_entries_returned(self):
  172. """Returns the maximum number of entries this filter will return if
  173. known, otherwise returns None.
  174. For example a simple state filter asking for `("m.room.create", "")`
  175. will return 1, whereas the default state filter will return None.
  176. This is used to bail out early if the right number of entries have been
  177. fetched.
  178. """
  179. if self.has_wildcards():
  180. return None
  181. return len(self.concrete_types())
  182. def filter_state(self, state_dict):
  183. """Returns the state filtered with by this StateFilter
  184. Args:
  185. state (dict[tuple[str, str], Any]): The state map to filter
  186. Returns:
  187. dict[tuple[str, str], Any]: The filtered state map
  188. """
  189. if self.is_full():
  190. return dict(state_dict)
  191. filtered_state = {}
  192. for k, v in iteritems(state_dict):
  193. typ, state_key = k
  194. if typ in self.types:
  195. state_keys = self.types[typ]
  196. if state_keys is None or state_key in state_keys:
  197. filtered_state[k] = v
  198. elif self.include_others:
  199. filtered_state[k] = v
  200. return filtered_state
  201. def is_full(self):
  202. """Whether this filter fetches everything or not
  203. Returns:
  204. bool
  205. """
  206. return self.include_others and not self.types
  207. def has_wildcards(self):
  208. """Whether the filter includes wildcards or is attempting to fetch
  209. specific state.
  210. Returns:
  211. bool
  212. """
  213. return self.include_others or any(
  214. state_keys is None for state_keys in itervalues(self.types)
  215. )
  216. def concrete_types(self):
  217. """Returns a list of concrete type/state_keys (i.e. not None) that
  218. will be fetched. This will be a complete list if `has_wildcards`
  219. returns False, but otherwise will be a subset (or even empty).
  220. Returns:
  221. list[tuple[str,str]]
  222. """
  223. return [
  224. (t, s)
  225. for t, state_keys in iteritems(self.types)
  226. if state_keys is not None
  227. for s in state_keys
  228. ]
  229. def get_member_split(self):
  230. """Return the filter split into two: one which assumes it's exclusively
  231. matching against member state, and one which assumes it's matching
  232. against non member state.
  233. This is useful due to the returned filters giving correct results for
  234. `is_full()`, `has_wildcards()`, etc, when operating against maps that
  235. either exclusively contain member events or only contain non-member
  236. events. (Which is the case when dealing with the member vs non-member
  237. state caches).
  238. Returns:
  239. tuple[StateFilter, StateFilter]: The member and non member filters
  240. """
  241. if EventTypes.Member in self.types:
  242. state_keys = self.types[EventTypes.Member]
  243. if state_keys is None:
  244. member_filter = StateFilter.all()
  245. else:
  246. member_filter = StateFilter({EventTypes.Member: state_keys})
  247. elif self.include_others:
  248. member_filter = StateFilter.all()
  249. else:
  250. member_filter = StateFilter.none()
  251. non_member_filter = StateFilter(
  252. types={k: v for k, v in iteritems(self.types) if k != EventTypes.Member},
  253. include_others=self.include_others,
  254. )
  255. return member_filter, non_member_filter
  256. class StateGroupStorage(object):
  257. """High level interface to fetching state for event.
  258. """
  259. def __init__(self, hs, stores):
  260. self.stores = stores
  261. def get_state_group_delta(self, state_group):
  262. """Given a state group try to return a previous group and a delta between
  263. the old and the new.
  264. Returns:
  265. Deferred[Tuple[Optional[int], Optional[list[dict[tuple[str, str], str]]]]]):
  266. (prev_group, delta_ids)
  267. """
  268. return self.stores.state.get_state_group_delta(state_group)
  269. @defer.inlineCallbacks
  270. def get_state_groups_ids(self, _room_id, event_ids):
  271. """Get the event IDs of all the state for the state groups for the given events
  272. Args:
  273. _room_id (str): id of the room for these events
  274. event_ids (iterable[str]): ids of the events
  275. Returns:
  276. Deferred[dict[int, dict[tuple[str, str], str]]]:
  277. dict of state_group_id -> (dict of (type, state_key) -> event id)
  278. """
  279. if not event_ids:
  280. return {}
  281. event_to_groups = yield self.stores.main._get_state_group_for_events(event_ids)
  282. groups = set(itervalues(event_to_groups))
  283. group_to_state = yield self.stores.state._get_state_for_groups(groups)
  284. return group_to_state
  285. @defer.inlineCallbacks
  286. def get_state_ids_for_group(self, state_group):
  287. """Get the event IDs of all the state in the given state group
  288. Args:
  289. state_group (int)
  290. Returns:
  291. Deferred[dict]: Resolves to a map of (type, state_key) -> event_id
  292. """
  293. group_to_state = yield self._get_state_for_groups((state_group,))
  294. return group_to_state[state_group]
  295. @defer.inlineCallbacks
  296. def get_state_groups(self, room_id, event_ids):
  297. """ Get the state groups for the given list of event_ids
  298. Returns:
  299. Deferred[dict[int, list[EventBase]]]:
  300. dict of state_group_id -> list of state events.
  301. """
  302. if not event_ids:
  303. return {}
  304. group_to_ids = yield self.get_state_groups_ids(room_id, event_ids)
  305. state_event_map = yield self.stores.main.get_events(
  306. [
  307. ev_id
  308. for group_ids in itervalues(group_to_ids)
  309. for ev_id in itervalues(group_ids)
  310. ],
  311. get_prev_content=False,
  312. )
  313. return {
  314. group: [
  315. state_event_map[v]
  316. for v in itervalues(event_id_map)
  317. if v in state_event_map
  318. ]
  319. for group, event_id_map in iteritems(group_to_ids)
  320. }
  321. def _get_state_groups_from_groups(self, groups, state_filter):
  322. """Returns the state groups for a given set of groups, filtering on
  323. types of state events.
  324. Args:
  325. groups(list[int]): list of state group IDs to query
  326. state_filter (StateFilter): The state filter used to fetch state
  327. from the database.
  328. Returns:
  329. Deferred[dict[int, dict[tuple[str, str], str]]]:
  330. dict of state_group_id -> (dict of (type, state_key) -> event id)
  331. """
  332. return self.stores.state._get_state_groups_from_groups(groups, state_filter)
  333. @defer.inlineCallbacks
  334. def get_state_for_events(self, event_ids, state_filter=StateFilter.all()):
  335. """Given a list of event_ids and type tuples, return a list of state
  336. dicts for each event.
  337. Args:
  338. event_ids (list[string])
  339. state_filter (StateFilter): The state filter used to fetch state
  340. from the database.
  341. Returns:
  342. deferred: A dict of (event_id) -> (type, state_key) -> [state_events]
  343. """
  344. event_to_groups = yield self.stores.main._get_state_group_for_events(event_ids)
  345. groups = set(itervalues(event_to_groups))
  346. group_to_state = yield self.stores.state._get_state_for_groups(
  347. groups, state_filter
  348. )
  349. state_event_map = yield self.stores.main.get_events(
  350. [ev_id for sd in itervalues(group_to_state) for ev_id in itervalues(sd)],
  351. get_prev_content=False,
  352. )
  353. event_to_state = {
  354. event_id: {
  355. k: state_event_map[v]
  356. for k, v in iteritems(group_to_state[group])
  357. if v in state_event_map
  358. }
  359. for event_id, group in iteritems(event_to_groups)
  360. }
  361. return {event: event_to_state[event] for event in event_ids}
  362. @defer.inlineCallbacks
  363. def get_state_ids_for_events(self, event_ids, state_filter=StateFilter.all()):
  364. """
  365. Get the state dicts corresponding to a list of events, containing the event_ids
  366. of the state events (as opposed to the events themselves)
  367. Args:
  368. event_ids(list(str)): events whose state should be returned
  369. state_filter (StateFilter): The state filter used to fetch state
  370. from the database.
  371. Returns:
  372. A deferred dict from event_id -> (type, state_key) -> event_id
  373. """
  374. event_to_groups = yield self.stores.main._get_state_group_for_events(event_ids)
  375. groups = set(itervalues(event_to_groups))
  376. group_to_state = yield self.stores.state._get_state_for_groups(
  377. groups, state_filter
  378. )
  379. event_to_state = {
  380. event_id: group_to_state[group]
  381. for event_id, group in iteritems(event_to_groups)
  382. }
  383. return {event: event_to_state[event] for event in event_ids}
  384. @defer.inlineCallbacks
  385. def get_state_for_event(self, event_id, state_filter=StateFilter.all()):
  386. """
  387. Get the state dict corresponding to a particular event
  388. Args:
  389. event_id(str): event whose state should be returned
  390. state_filter (StateFilter): The state filter used to fetch state
  391. from the database.
  392. Returns:
  393. A deferred dict from (type, state_key) -> state_event
  394. """
  395. state_map = yield self.get_state_for_events([event_id], state_filter)
  396. return state_map[event_id]
  397. @defer.inlineCallbacks
  398. def get_state_ids_for_event(self, event_id, state_filter=StateFilter.all()):
  399. """
  400. Get the state dict corresponding to a particular event
  401. Args:
  402. event_id(str): event whose state should be returned
  403. state_filter (StateFilter): The state filter used to fetch state
  404. from the database.
  405. Returns:
  406. A deferred dict from (type, state_key) -> state_event
  407. """
  408. state_map = yield self.get_state_ids_for_events([event_id], state_filter)
  409. return state_map[event_id]
  410. def _get_state_for_groups(self, groups, state_filter=StateFilter.all()):
  411. """Gets the state at each of a list of state groups, optionally
  412. filtering by type/state_key
  413. Args:
  414. groups (iterable[int]): list of state groups for which we want
  415. to get the state.
  416. state_filter (StateFilter): The state filter used to fetch state
  417. from the database.
  418. Returns:
  419. Deferred[dict[int, dict[tuple[str, str], str]]]:
  420. dict of state_group_id -> (dict of (type, state_key) -> event id)
  421. """
  422. return self.stores.state._get_state_for_groups(groups, state_filter)
  423. def store_state_group(
  424. self, event_id, room_id, prev_group, delta_ids, current_state_ids
  425. ):
  426. """Store a new set of state, returning a newly assigned state group.
  427. Args:
  428. event_id (str): The event ID for which the state was calculated
  429. room_id (str)
  430. prev_group (int|None): A previous state group for the room, optional.
  431. delta_ids (dict|None): The delta between state at `prev_group` and
  432. `current_state_ids`, if `prev_group` was given. Same format as
  433. `current_state_ids`.
  434. current_state_ids (dict): The state to store. Map of (type, state_key)
  435. to event_id.
  436. Returns:
  437. Deferred[int]: The state group ID
  438. """
  439. return self.stores.state.store_state_group(
  440. event_id, room_id, prev_group, delta_ids, current_state_ids
  441. )