@defer.inlineCallbacks
    def incremental_sync_with_gap(self, sync_config, since_token):
        now_token = yield self.event_sources.get_current_token()

        rooms = yield self.store.get_rooms_for_user(sync_config.user.to_string())
        room_ids = [room.room_id for room in rooms]

        presence_source = self.event_sources.sources["presence"]
        presence, presence_key = yield presence_source.get_new_events(
            user=sync_config.user,
            from_key=since_token.presence_key,
            limit=sync_config.filter_collection.presence_limit(),
            room_ids=room_ids,
            is_guest=sync_config.is_guest,
        )
        now_token = now_token.copy_and_replace("presence_key", presence_key)

        now_token, ephemeral_by_room = yield self.ephemeral_by_room(
            sync_config, now_token, since_token
        )

        rm_handler = self.hs.get_handlers().room_member_handler
        app_service = yield self.store.get_app_service_by_user_id(
            sync_config.user.to_string()
        )
        if app_service:
            rooms = yield self.store.get_app_service_rooms(app_service)
            joined_room_ids = set(r.room_id for r in rooms)
        else:
            joined_room_ids = yield rm_handler.get_joined_rooms_for_user(
                sync_config.user
            )

        user_id = sync_config.user.to_string()

        timeline_limit = sync_config.filter_collection.timeline_limit()

        tags_by_room = yield self.store.get_updated_tags(
            user_id,
            since_token.account_data_key,
        )

        account_data, account_data_by_room = (
            yield self.store.get_updated_account_data_for_user(
                user_id,
                since_token.account_data_key,
            )
        )

        push_rules_changed = yield self.store.have_push_rules_changed_for_user(
            user_id, int(since_token.push_rules_key)
        )

        if push_rules_changed:
            account_data["m.push_rules"] = yield self.push_rules_for_user(
                sync_config.user
            )

        # Get a list of membership change events that have happened.
        rooms_changed = yield self.store.get_membership_changes_for_user(
            user_id, since_token.room_key, now_token.room_key
        )

        mem_change_events_by_room_id = {}
        for event in rooms_changed:
            mem_change_events_by_room_id.setdefault(event.room_id, []).append(event)

        newly_joined_rooms = []
        archived = []
        invited = []
        for room_id, events in mem_change_events_by_room_id.items():
            non_joins = [e for e in events if e.membership != Membership.JOIN]
            has_join = len(non_joins) != len(events)

            # We want to figure out if we joined the room at some point since
            # the last sync (even if we have since left). This is to make sure
            # we do send down the room, and with full state, where necessary
            if room_id in joined_room_ids or has_join:
                old_state = yield self.get_state_at(room_id, since_token)
                old_mem_ev = old_state.get((EventTypes.Member, user_id), None)
                if not old_mem_ev or old_mem_ev.membership != Membership.JOIN:
                        newly_joined_rooms.append(room_id)

                if room_id in joined_room_ids:
                    continue

            if not non_joins:
                continue

            # Only bother if we're still currently invited
            should_invite = non_joins[-1].membership == Membership.INVITE
            if should_invite:
                room_sync = InvitedSyncResult(room_id, invite=non_joins[-1])
                if room_sync:
                    invited.append(room_sync)

            # Always include leave/ban events. Just take the last one.
            # TODO: How do we handle ban -> leave in same batch?
            leave_events = [
                e for e in non_joins
                if e.membership in (Membership.LEAVE, Membership.BAN)
            ]

            if leave_events:
                leave_event = leave_events[-1]
                room_sync = yield self.incremental_sync_for_archived_room(
                    sync_config, room_id, leave_event.event_id, since_token,
                    tags_by_room, account_data_by_room,
                    full_state=room_id in newly_joined_rooms
                )
                if room_sync:
                    archived.append(room_sync)

        # Get all events for rooms we're currently joined to.
        room_to_events = yield self.store.get_room_events_stream_for_rooms(
            room_ids=joined_room_ids,
            from_key=since_token.room_key,
            to_key=now_token.room_key,
            limit=timeline_limit + 1,
        )

        joined = []
        # We loop through all room ids, even if there are no new events, in case
        # there are non room events taht we need to notify about.
        for room_id in joined_room_ids:
            room_entry = room_to_events.get(room_id, None)

            if room_entry:
                events, start_key = room_entry

                prev_batch_token = now_token.copy_and_replace("room_key", start_key)

                newly_joined_room = room_id in newly_joined_rooms
                full_state = newly_joined_room

                batch = yield self.load_filtered_recents(
                    room_id, sync_config, prev_batch_token,
                    since_token=since_token,
                    recents=events,
                    newly_joined_room=newly_joined_room,
                )
            else:
                batch = TimelineBatch(
                    events=[],
                    prev_batch=since_token,
                    limited=False,
                )
                full_state = False

            room_sync = yield self.incremental_sync_with_gap_for_room(
                room_id=room_id,
                sync_config=sync_config,
                since_token=since_token,
                now_token=now_token,
                ephemeral_by_room=ephemeral_by_room,
                tags_by_room=tags_by_room,
                account_data_by_room=account_data_by_room,
                batch=batch,
                full_state=full_state,
            )
            if room_sync:
                joined.append(room_sync)

        # For each newly joined room, we want to send down presence of
        # existing users.
        presence_handler = self.hs.get_handlers().presence_handler
        extra_presence_users = set()
        for room_id in newly_joined_rooms:
            users = yield self.store.get_users_in_room(event.room_id)
            extra_presence_users.update(users)

        # For each new member, send down presence.
        for joined_sync in joined:
            it = itertools.chain(joined_sync.timeline.events, joined_sync.state.values())
            for event in it:
                if event.type == EventTypes.Member:
                    if event.membership == Membership.JOIN:
                        extra_presence_users.add(event.state_key)

        states = yield presence_handler.get_states(
            [u for u in extra_presence_users if u != user_id],
            as_event=True,
        )
        presence.extend(states)

        account_data_for_user = sync_config.filter_collection.filter_account_data(
            self.account_data_for_user(account_data)
        )

        presence = sync_config.filter_collection.filter_presence(
            presence
        )

        defer.returnValue(SyncResult(
            presence=presence,
            account_data=account_data_for_user,
            joined=joined,
            invited=invited,
            archived=archived,
            next_batch=now_token,
        ))
Get the incremental delta needed to bring the client up to
dataset/ETHPy150Open matrix-org/synapse/synapse/handlers/sync.py/SyncHandler.incremental_sync_with_gap