From 3bf4d5e4654ed64bda5b0407f3ff821e3fcdeaf6 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 5 Mar 2018 16:26:11 +0000 Subject: [PATCH] Add CompactEvent --- synapse/events/__init__.py | 202 ++++++++++++++++++++++++++++++- synapse/handlers/message.py | 2 + synapse/storage/events.py | 4 +- synapse/storage/events_worker.py | 6 +- 4 files changed, 207 insertions(+), 7 deletions(-) diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py index 95f0bdf3e9..d56f45bbf5 100644 --- a/synapse/events/__init__.py +++ b/synapse/events/__init__.py @@ -13,10 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from synapse.util.frozenutils import freeze -from synapse.util.caches import intern_dict +from synapse.util.frozenutils import freeze, unfreeze +from synapse.util.caches import intern_dict, intern_string import abc +import simplejson as json # Whether we should use frozen_dict in FrozenEvent. Using frozen_dicts prevents @@ -229,3 +230,200 @@ class FrozenEvent(EventBase): self.get("type", None), self.get("state_key", None), ) + + +def _compact_property(key): + def getter(self): + try: + return self[key] + except KeyError: + raise AttributeError( + "AttributeError: '%s' object has no attribute '%s'" % ( + self.__name__, key, + ) + ) + + return property(getter) + + +class _Unsigned(object): + __slots__ = [ + "age_ts", + "replaces_state", + "redacted_because", + "invite_room_state", + "prev_content", + "prev_sender", + "redacted_by", + ] + + def __init__(self, **kwargs): + for s in self.__slots__: + try: + setattr(self, s, kwargs[s]) + except KeyError: + continue + + def __getitem__(self, field): + try: + return getattr(self, field) + except AttributeError: + raise KeyError(field) + + def __setitem__(self, field, value): + try: + setattr(self, field, value) + except AttributeError: + raise KeyError(field) + + def __delitem__(self, field): + try: + return delattr(self, field) + except AttributeError: + raise KeyError(field) + + def __contains__(self, field): + return hasattr(self, field) + + def get(self, key, default=None): + return getattr(self, key, default) + + def pop(self, key, default): + r = self.get(key, default) + try: + delattr(self, key) + except AttributeError: + pass + return r + + def __iter__(self): + for key in self.__slots__: + if hasattr(self, key): + yield (key, getattr(self, key)) + + +class CompactEvent(EventBase): + __slots__ = [ + "event_json", + + "internal_metadata", + "rejected_reason", + + "signatures", + "unsigned", + + "event_id", + "room_id", + "type", + "state_key", + "sender", + ] + + def __init__(self, event_dict, internal_metadata_dict={}, rejected_reason=None): + event_dict = dict(unfreeze(event_dict)) + + object.__setattr__(self, "unsigned", _Unsigned(**event_dict.pop("unsigned", {}))) + + signatures = { + intern_string(name): { + intern_string(sig_id): sig.encode("utf-8") + for sig_id, sig in sigs.iteritems() + } + for name, sigs in event_dict.pop("signatures", {}).iteritems() + } + object.__setattr__(self, "signatures", signatures) + + object.__setattr__(self, "event_json", json.dumps(event_dict)) + + object.__setattr__(self, "rejected_reason", rejected_reason) + object.__setattr__(self, "internal_metadata", _EventInternalMetadata( + internal_metadata_dict + )) + + object.__setattr__(self, "event_id", event_dict["event_id"]) + object.__setattr__(self, "room_id", event_dict["room_id"]) + object.__setattr__(self, "type", event_dict["type"]) + if "state_key" in event_dict: + object.__setattr__(self, "state_key", event_dict["state_key"]) + object.__setattr__(self, "sender", event_dict["sender"]) + + auth_events = _compact_property("auth_events") + depth = _compact_property("depth") + content = _compact_property("content") + hashes = _compact_property("hashes") + origin = _compact_property("origin") + origin_server_ts = _compact_property("origin_server_ts") + prev_events = _compact_property("prev_events") + prev_state = _compact_property("prev_state") + redacts = _compact_property("redacts") + + @property + def user_id(self): + return self.sender + + @property + def membership(self): + return self.content["membership"] + + def is_state(self): + return hasattr(self, "state_key") and self.state_key is not None + + def get_dict(self): + d = json.loads(self.event_json) + d.update({ + "signatures": dict(self.signatures), + "unsigned": dict(self.unsigned), + }) + + return d + + def get(self, key, default=None): + if key in self.__slots__: + return freeze(getattr(self, key, default)) + + d = json.loads(self.event_json) + return d.get(key, default) + + def get_internal_metadata_dict(self): + return self.internal_metadata.get_dict() + + def __getitem__(self, field): + if field in self.__slots__: + try: + return freeze(getattr(self, field)) + except AttributeError: + raise KeyError(field) + + d = json.loads(self.event_json) + return d[field] + + def __contains__(self, field): + if field in self.__slots__: + return hasattr(self, field) + + d = json.loads(self.event_json) + return field in d + + @staticmethod + def from_event(event): + return CompactEvent( + event.get_pdu_json(), + event.get_internal_metadata_dict(), + event.rejected_reason, + ) + + def __str__(self): + return self.__repr__() + + def __repr__(self): + return "" % ( + self.get("event_id", None), + self.get("type", None), + self.get("state_key", None), + ) + + def iteritems(self): + return json.loads(self.event_json).iteritems() + + def __eq__(self, other): + return self.event_id == other.event_id diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 1ed0ffcc51..d017aa5963 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -261,6 +261,8 @@ class MessageHandler(BaseHandler): time_now = self.clock.time_msec() + logger.info("Returning event: %r", events) + chunk = { "chunk": [ serialize_event(e, time_now, as_client_event) diff --git a/synapse/storage/events.py b/synapse/storage/events.py index 85ce6bea1a..f9cf9abaed 100644 --- a/synapse/storage/events.py +++ b/synapse/storage/events.py @@ -18,7 +18,7 @@ from synapse.storage.events_worker import EventsWorkerStore from twisted.internet import defer -from synapse.events import USE_FROZEN_DICTS +from synapse.events import USE_FROZEN_DICTS, CompactEvent from synapse.util.async import ObservableDeferred from synapse.util.logcontext import ( @@ -1254,7 +1254,7 @@ class EventsStore(EventsWorkerStore): event = ev_map[row["event_id"]] if not row["rejects"] and not row["redacts"]: to_prefill.append(_EventCacheEntry( - event=event, + event=CompactEvent.from_event(event), redacted_event=None, )) diff --git a/synapse/storage/events_worker.py b/synapse/storage/events_worker.py index 2e23dd78ba..79c0c630b2 100644 --- a/synapse/storage/events_worker.py +++ b/synapse/storage/events_worker.py @@ -16,7 +16,7 @@ from ._base import SQLBaseStore from twisted.internet import defer, reactor -from synapse.events import FrozenEvent +from synapse.events import CompactEvent from synapse.events.utils import prune_event from synapse.util.logcontext import ( @@ -69,7 +69,7 @@ class EventsWorkerStore(SQLBaseStore): False throw an exception. Returns: - Deferred : A FrozenEvent. + Deferred : A CompactEvent. """ events = yield self._get_events( [event_id], @@ -354,7 +354,7 @@ class EventsWorkerStore(SQLBaseStore): desc="_get_event_from_row_rejected_reason", ) - original_ev = FrozenEvent( + original_ev = CompactEvent( d, internal_metadata_dict=internal_metadata, rejected_reason=rejected_reason,