apply_event: Fix broken deepcopy attempt for subs.

When we were getting an apply_event call for
a subscription/add event, we were trying not to
mutate the event itself, but this clumsy code
was still mutating the actual event:

    # Avoid letting 'subscribers' entries end up in the list
    for i, sub in enumerate(event['subscriptions']):
        event['subscriptions'][i] = \
            copy.deepcopy(event['subscriptions'][i])
        del event['subscriptions'][i]['subscribers']

This is only a theoretical bug.

The only person who receives a subscription/add
event is the current user.

And it wouldn't have affected the current user,
since the apply_event was correctly updating the
state, and we wouldn't actually deliver the event
to the client (because the whole point of apply_event
is to prevent us from having to piggyback the
super-recent events on to our payload or put
them into the event queue and possibly race).

The new code just cleanly makes a copy of each
sub, if necessary, as we add them to state["subscriptions"].

And I updated the event schemas to reflect that
subscribers is always present in subscription/add
event.

Long term we should probably avoid sending subscribers
on this event when the clients don't set something
like include_subscribers.  That's a fairly complicated
fix that involves passing in flags to ClientDescriptor.
Alternatively, we could just say that our policy is
that we never send subscribers there, but we instead
use peer_add events.  See issue #17089 for more
details.
This commit is contained in:
Steve Howell
2021-01-20 14:14:52 +00:00
committed by Tim Abbott
parent c6acde9c63
commit 1498b2ef69
3 changed files with 17 additions and 29 deletions

View File

@@ -703,17 +703,18 @@ def apply_event(
state['realm_email_auth_enabled'] = value['Email']
elif event['type'] == "subscription":
if event["op"] == "add":
if not include_subscribers:
# Avoid letting 'subscribers' entries end up in the list
for i, sub in enumerate(event['subscriptions']):
event['subscriptions'][i] = copy.deepcopy(event['subscriptions'][i])
del event['subscriptions'][i]['subscribers']
added_stream_ids = {sub["stream_id"] for sub in event["subscriptions"]}
was_added = lambda s: s["stream_id"] in added_stream_ids
existing_stream_ids = {sub["stream_id"] for sub in state["subscriptions"]}
# add the new subscriptions
state['subscriptions'] += event['subscriptions']
for sub in event["subscriptions"]:
if sub["stream_id"] not in existing_stream_ids:
if "subscribers" in sub and not include_subscribers:
sub = copy.deepcopy(sub)
del sub["subscribers"]
state["subscriptions"].append(sub)
# remove them from unsubscribed if they had been there
state['unsubscribed'] = [s for s in state['unsubscribed'] if not was_added(s)]