You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

493 lines
19 KiB

  1. # Copyright 2014-2016 OpenMarket Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from abc import ABC, abstractmethod
  15. from typing import TYPE_CHECKING, List, Optional, Tuple
  16. import attr
  17. from immutabledict import immutabledict
  18. from synapse.appservice import ApplicationService
  19. from synapse.events import EventBase
  20. from synapse.types import JsonDict, StateMap
  21. if TYPE_CHECKING:
  22. from synapse.storage.controllers import StorageControllers
  23. from synapse.storage.databases import StateGroupDataStore
  24. from synapse.storage.databases.main import DataStore
  25. from synapse.types.state import StateFilter
  26. class UnpersistedEventContextBase(ABC):
  27. """
  28. This is a base class for EventContext and UnpersistedEventContext, objects which
  29. hold information relevant to storing an associated event. Note that an
  30. UnpersistedEventContexts must be converted into an EventContext before it is
  31. suitable to send to the db with its associated event.
  32. Attributes:
  33. _storage: storage controllers for interfacing with the database
  34. app_service: If the associated event is being sent by a (local) application service, that
  35. app service.
  36. """
  37. def __init__(self, storage_controller: "StorageControllers"):
  38. self._storage: "StorageControllers" = storage_controller
  39. self.app_service: Optional[ApplicationService] = None
  40. @abstractmethod
  41. async def persist(
  42. self,
  43. event: EventBase,
  44. ) -> "EventContext":
  45. """
  46. A method to convert an UnpersistedEventContext to an EventContext, suitable for
  47. sending to the database with the associated event.
  48. """
  49. pass
  50. @abstractmethod
  51. async def get_prev_state_ids(
  52. self, state_filter: Optional["StateFilter"] = None
  53. ) -> StateMap[str]:
  54. """
  55. Gets the room state at the event (ie not including the event if the event is a
  56. state event).
  57. Args:
  58. state_filter: specifies the type of state event to fetch from DB, example:
  59. EventTypes.JoinRules
  60. """
  61. pass
  62. @attr.s(slots=True, auto_attribs=True)
  63. class EventContext(UnpersistedEventContextBase):
  64. """
  65. Holds information relevant to persisting an event
  66. Attributes:
  67. rejected: A rejection reason if the event was rejected, else None
  68. _state_group: The ID of the state group for this event. Note that state events
  69. are persisted with a state group which includes the new event, so this is
  70. effectively the state *after* the event in question.
  71. For a *rejected* state event, where the state of the rejected event is
  72. ignored, this state_group should never make it into the
  73. event_to_state_groups table. Indeed, inspecting this value for a rejected
  74. state event is almost certainly incorrect.
  75. For an outlier, where we don't have the state at the event, this will be
  76. None.
  77. Note that this is a private attribute: it should be accessed via
  78. the ``state_group`` property.
  79. state_group_before_event: The ID of the state group representing the state
  80. of the room before this event.
  81. If this is a non-state event, this will be the same as ``state_group``. If
  82. it's a state event, it will be the same as ``prev_group``.
  83. If ``state_group`` is None (ie, the event is an outlier),
  84. ``state_group_before_event`` will always also be ``None``.
  85. state_delta_due_to_event: If `state_group` and `state_group_before_event` are not None
  86. then this is the delta of the state between the two groups.
  87. prev_group: If it is known, ``state_group``'s prev_group. Note that this being
  88. None does not necessarily mean that ``state_group`` does not have
  89. a prev_group!
  90. If the event is a state event, this is normally the same as
  91. ``state_group_before_event``.
  92. If ``state_group`` is None (ie, the event is an outlier), ``prev_group``
  93. will always also be ``None``.
  94. Note that this *not* (necessarily) the state group associated with
  95. ``_prev_state_ids``.
  96. delta_ids: If ``prev_group`` is not None, the state delta between ``prev_group``
  97. and ``state_group``.
  98. partial_state: if True, we may be storing this event with a temporary,
  99. incomplete state.
  100. """
  101. _storage: "StorageControllers"
  102. rejected: Optional[str] = None
  103. _state_group: Optional[int] = None
  104. state_group_before_event: Optional[int] = None
  105. _state_delta_due_to_event: Optional[StateMap[str]] = None
  106. prev_group: Optional[int] = None
  107. delta_ids: Optional[StateMap[str]] = None
  108. app_service: Optional[ApplicationService] = None
  109. partial_state: bool = False
  110. @staticmethod
  111. def with_state(
  112. storage: "StorageControllers",
  113. state_group: Optional[int],
  114. state_group_before_event: Optional[int],
  115. state_delta_due_to_event: Optional[StateMap[str]],
  116. partial_state: bool,
  117. prev_group: Optional[int] = None,
  118. delta_ids: Optional[StateMap[str]] = None,
  119. ) -> "EventContext":
  120. return EventContext(
  121. storage=storage,
  122. state_group=state_group,
  123. state_group_before_event=state_group_before_event,
  124. state_delta_due_to_event=state_delta_due_to_event,
  125. prev_group=prev_group,
  126. delta_ids=delta_ids,
  127. partial_state=partial_state,
  128. )
  129. @staticmethod
  130. def for_outlier(
  131. storage: "StorageControllers",
  132. ) -> "EventContext":
  133. """Return an EventContext instance suitable for persisting an outlier event"""
  134. return EventContext(storage=storage)
  135. async def persist(self, event: EventBase) -> "EventContext":
  136. return self
  137. async def serialize(self, event: EventBase, store: "DataStore") -> JsonDict:
  138. """Converts self to a type that can be serialized as JSON, and then
  139. deserialized by `deserialize`
  140. Args:
  141. event: The event that this context relates to
  142. Returns:
  143. The serialized event.
  144. """
  145. return {
  146. "state_group": self._state_group,
  147. "state_group_before_event": self.state_group_before_event,
  148. "rejected": self.rejected,
  149. "prev_group": self.prev_group,
  150. "state_delta_due_to_event": _encode_state_dict(
  151. self._state_delta_due_to_event
  152. ),
  153. "delta_ids": _encode_state_dict(self.delta_ids),
  154. "app_service_id": self.app_service.id if self.app_service else None,
  155. "partial_state": self.partial_state,
  156. }
  157. @staticmethod
  158. def deserialize(storage: "StorageControllers", input: JsonDict) -> "EventContext":
  159. """Converts a dict that was produced by `serialize` back into a
  160. EventContext.
  161. Args:
  162. storage: Used to convert AS ID to AS object and fetch state.
  163. input: A dict produced by `serialize`
  164. Returns:
  165. The event context.
  166. """
  167. context = EventContext(
  168. # We use the state_group and prev_state_id stuff to pull the
  169. # current_state_ids out of the DB and construct prev_state_ids.
  170. storage=storage,
  171. state_group=input["state_group"],
  172. state_group_before_event=input["state_group_before_event"],
  173. prev_group=input["prev_group"],
  174. state_delta_due_to_event=_decode_state_dict(
  175. input["state_delta_due_to_event"]
  176. ),
  177. delta_ids=_decode_state_dict(input["delta_ids"]),
  178. rejected=input["rejected"],
  179. partial_state=input.get("partial_state", False),
  180. )
  181. app_service_id = input["app_service_id"]
  182. if app_service_id:
  183. context.app_service = storage.main.get_app_service_by_id(app_service_id)
  184. return context
  185. @property
  186. def state_group(self) -> Optional[int]:
  187. """The ID of the state group for this event.
  188. Note that state events are persisted with a state group which includes the new
  189. event, so this is effectively the state *after* the event in question.
  190. For an outlier, where we don't have the state at the event, this will be None.
  191. It is an error to access this for a rejected event, since rejected state should
  192. not make it into the room state. Accessing this property will raise an exception
  193. if ``rejected`` is set.
  194. """
  195. if self.rejected:
  196. raise RuntimeError("Attempt to access state_group of rejected event")
  197. return self._state_group
  198. async def get_current_state_ids(
  199. self, state_filter: Optional["StateFilter"] = None
  200. ) -> Optional[StateMap[str]]:
  201. """
  202. Gets the room state map, including this event - ie, the state in ``state_group``
  203. It is an error to access this for a rejected event, since rejected state should
  204. not make it into the room state. This method will raise an exception if
  205. ``rejected`` is set.
  206. Arg:
  207. state_filter: specifies the type of state event to fetch from DB, example: EventTypes.JoinRules
  208. Returns:
  209. Returns None if state_group is None, which happens when the associated
  210. event is an outlier.
  211. Maps a (type, state_key) to the event ID of the state event matching
  212. this tuple.
  213. """
  214. if self.rejected:
  215. raise RuntimeError("Attempt to access state_ids of rejected event")
  216. assert self._state_delta_due_to_event is not None
  217. prev_state_ids = await self.get_prev_state_ids(state_filter)
  218. if self._state_delta_due_to_event:
  219. prev_state_ids = dict(prev_state_ids)
  220. prev_state_ids.update(self._state_delta_due_to_event)
  221. return prev_state_ids
  222. async def get_prev_state_ids(
  223. self, state_filter: Optional["StateFilter"] = None
  224. ) -> StateMap[str]:
  225. """
  226. Gets the room state map, excluding this event.
  227. For a non-state event, this will be the same as get_current_state_ids().
  228. Args:
  229. state_filter: specifies the type of state event to fetch from DB, example: EventTypes.JoinRules
  230. Returns:
  231. Returns {} if state_group is None, which happens when the associated
  232. event is an outlier.
  233. Maps a (type, state_key) to the event ID of the state event matching
  234. this tuple.
  235. """
  236. assert self.state_group_before_event is not None
  237. return await self._storage.state.get_state_ids_for_group(
  238. self.state_group_before_event, state_filter
  239. )
  240. @attr.s(slots=True, auto_attribs=True)
  241. class UnpersistedEventContext(UnpersistedEventContextBase):
  242. """
  243. The event context holds information about the state groups for an event. It is important
  244. to remember that an event technically has two state groups: the state group before the
  245. event, and the state group after the event. If the event is not a state event, the state
  246. group will not change (ie the state group before the event will be the same as the state
  247. group after the event), but if it is a state event the state group before the event
  248. will differ from the state group after the event.
  249. This is a version of an EventContext before the new state group (if any) has been
  250. computed and stored. It contains information about the state before the event (which
  251. also may be the information after the event, if the event is not a state event). The
  252. UnpersistedEventContext must be converted into an EventContext by calling the method
  253. 'persist' on it before it is suitable to be sent to the DB for processing.
  254. state_group_after_event:
  255. The state group after the event. This will always be None until it is persisted.
  256. If the event is not a state event, this will be the same as
  257. state_group_before_event.
  258. state_group_before_event:
  259. The ID of the state group representing the state of the room before this event.
  260. state_delta_due_to_event:
  261. If the event is a state event, then this is the delta of the state between
  262. `state_group` and `state_group_before_event`
  263. prev_group_for_state_group_before_event:
  264. If it is known, ``state_group_before_event``'s previous state group.
  265. delta_ids_to_state_group_before_event:
  266. If ``prev_group_for_state_group_before_event`` is not None, the state delta
  267. between ``prev_group_for_state_group_before_event`` and ``state_group_before_event``.
  268. partial_state:
  269. Whether the event has partial state.
  270. state_map_before_event:
  271. A map of the state before the event, i.e. the state at `state_group_before_event`
  272. """
  273. _storage: "StorageControllers"
  274. state_group_before_event: Optional[int]
  275. state_group_after_event: Optional[int]
  276. state_delta_due_to_event: Optional[dict]
  277. prev_group_for_state_group_before_event: Optional[int]
  278. delta_ids_to_state_group_before_event: Optional[StateMap[str]]
  279. partial_state: bool
  280. state_map_before_event: Optional[StateMap[str]] = None
  281. @classmethod
  282. async def batch_persist_unpersisted_contexts(
  283. cls,
  284. events_and_context: List[Tuple[EventBase, "UnpersistedEventContextBase"]],
  285. room_id: str,
  286. last_known_state_group: int,
  287. datastore: "StateGroupDataStore",
  288. ) -> List[Tuple[EventBase, EventContext]]:
  289. """
  290. Takes a list of events and their associated unpersisted contexts and persists
  291. the unpersisted contexts, returning a list of events and persisted contexts.
  292. Note that all the events must be in a linear chain (ie a <- b <- c).
  293. Args:
  294. events_and_context: A list of events and their unpersisted contexts
  295. room_id: the room_id for the events
  296. last_known_state_group: the last persisted state group
  297. datastore: a state datastore
  298. """
  299. amended_events_and_context = await datastore.store_state_deltas_for_batched(
  300. events_and_context, room_id, last_known_state_group
  301. )
  302. events_and_persisted_context = []
  303. for event, unpersisted_context in amended_events_and_context:
  304. if event.is_state():
  305. context = EventContext(
  306. storage=unpersisted_context._storage,
  307. state_group=unpersisted_context.state_group_after_event,
  308. state_group_before_event=unpersisted_context.state_group_before_event,
  309. state_delta_due_to_event=unpersisted_context.state_delta_due_to_event,
  310. partial_state=unpersisted_context.partial_state,
  311. prev_group=unpersisted_context.state_group_before_event,
  312. delta_ids=unpersisted_context.state_delta_due_to_event,
  313. )
  314. else:
  315. context = EventContext(
  316. storage=unpersisted_context._storage,
  317. state_group=unpersisted_context.state_group_after_event,
  318. state_group_before_event=unpersisted_context.state_group_before_event,
  319. state_delta_due_to_event=unpersisted_context.state_delta_due_to_event,
  320. partial_state=unpersisted_context.partial_state,
  321. prev_group=unpersisted_context.prev_group_for_state_group_before_event,
  322. delta_ids=unpersisted_context.delta_ids_to_state_group_before_event,
  323. )
  324. events_and_persisted_context.append((event, context))
  325. return events_and_persisted_context
  326. async def get_prev_state_ids(
  327. self, state_filter: Optional["StateFilter"] = None
  328. ) -> StateMap[str]:
  329. """
  330. Gets the room state map, excluding this event.
  331. Args:
  332. state_filter: specifies the type of state event to fetch from DB
  333. Returns:
  334. Maps a (type, state_key) to the event ID of the state event matching
  335. this tuple.
  336. """
  337. if self.state_map_before_event:
  338. return self.state_map_before_event
  339. assert self.state_group_before_event is not None
  340. return await self._storage.state.get_state_ids_for_group(
  341. self.state_group_before_event, state_filter
  342. )
  343. async def persist(self, event: EventBase) -> EventContext:
  344. """
  345. Creates a full `EventContext` for the event, persisting any referenced state that
  346. has not yet been persisted.
  347. Args:
  348. event: event that the EventContext is associated with.
  349. Returns: An EventContext suitable for sending to the database with the event
  350. for persisting
  351. """
  352. assert self.partial_state is not None
  353. # If we have a full set of state for before the event but don't have a state
  354. # group for that state, we need to get one
  355. if self.state_group_before_event is None:
  356. assert self.state_map_before_event
  357. state_group_before_event = await self._storage.state.store_state_group(
  358. event.event_id,
  359. event.room_id,
  360. prev_group=self.prev_group_for_state_group_before_event,
  361. delta_ids=self.delta_ids_to_state_group_before_event,
  362. current_state_ids=self.state_map_before_event,
  363. )
  364. self.state_group_before_event = state_group_before_event
  365. # if the event isn't a state event the state group doesn't change
  366. if not self.state_delta_due_to_event:
  367. state_group_after_event = self.state_group_before_event
  368. # otherwise if it is a state event we need to get a state group for it
  369. else:
  370. state_group_after_event = await self._storage.state.store_state_group(
  371. event.event_id,
  372. event.room_id,
  373. prev_group=self.state_group_before_event,
  374. delta_ids=self.state_delta_due_to_event,
  375. current_state_ids=None,
  376. )
  377. return EventContext.with_state(
  378. storage=self._storage,
  379. state_group=state_group_after_event,
  380. state_group_before_event=self.state_group_before_event,
  381. state_delta_due_to_event=self.state_delta_due_to_event,
  382. partial_state=self.partial_state,
  383. prev_group=self.state_group_before_event,
  384. delta_ids=self.state_delta_due_to_event,
  385. )
  386. def _encode_state_dict(
  387. state_dict: Optional[StateMap[str]],
  388. ) -> Optional[List[Tuple[str, str, str]]]:
  389. """Since dicts of (type, state_key) -> event_id cannot be serialized in
  390. JSON we need to convert them to a form that can.
  391. """
  392. if state_dict is None:
  393. return None
  394. return [(etype, state_key, v) for (etype, state_key), v in state_dict.items()]
  395. def _decode_state_dict(
  396. input: Optional[List[Tuple[str, str, str]]]
  397. ) -> Optional[StateMap[str]]:
  398. """Decodes a state dict encoded using `_encode_state_dict` above"""
  399. if input is None:
  400. return None
  401. return immutabledict({(etype, state_key): v for etype, state_key, v in input})