# Buggy code
def _populate_stats_process_rooms(self, progress, batch_size):
    if not self.stats_enabled:
        yield self._end_background_update("populate_stats_process_rooms")
        defer.returnValue(1)

    # If we don't have progress filed, delete everything.
    if not progress:
        yield self.delete_all_stats()

    def _get_next_batch(txn):
        # Only fetch 250 rooms, so we don't fetch too many at once, even
        # if those 250 rooms have less than batch_size state events.
        sql = """
                SELECT room_id, events FROM %s_rooms
                ORDER BY events DESC
                LIMIT 250
            """ % (TEMP_TABLE,)
        txn.execute(sql)
        rooms_to_work_on = txn.fetchall()

        if not rooms_to_work_on:
            return None

        # Get how many are left to process, so we can give status on how
        # far we are in processing
        txn.execute("SELECT COUNT(*) FROM " + TEMP_TABLE + "_rooms")
        progress["remaining"] = txn.fetchone()[0]

        return rooms_to_work_on

    rooms_to_work_on = yield self.runInteraction(
        "populate_stats_temp_read", _get_next_batch
    )

    # No more rooms -- complete the transaction.
    if not rooms_to_work_on:
        yield self._end_background_update("populate_stats_process_rooms")
        defer.returnValue(1)

    logger.info(
        "Processing the next %d rooms of %d remaining",
        (len(rooms_to_work_on), progress["remaining"]),
    )

    # Number of state events we've processed by going through each room
    processed_event_count = 0

    for room_id, event_count in rooms_to_work_on:
        current_state_ids = yield self.get_current_state_ids(room_id)

        join_rules = yield self.get_event(
            current_state_ids.get((EventTypes.JoinRules, "")), allow_none=True
        )
        history_visibility = yield self.get_event(
            current_state_ids.get((EventTypes.RoomHistoryVisibility, "")),
            allow_none=True,
        )
        encryption = yield self.get_event(
            current_state_ids.get((EventTypes.RoomEncryption, "")), allow_none=True
        )
        name = yield self.get_event(
            current_state_ids.get((EventTypes.Name, "")), allow_none=True
        )
        topic = yield self.get_event(
            current_state_ids.get((EventTypes.Topic, "")), allow_none=True
        )
        avatar = yield self.get_event(
            current_state_ids.get((EventTypes.RoomAvatar, "")), allow_none=True
        )
        canonical_alias = yield self.get_event(
            current_state_ids.get((EventTypes.CanonicalAlias, "")), allow_none=True
        )

        def _or_none(x, arg):
            if x:
                return x.content.get(arg)
            return None

        yield self.update_room_state(
            room_id,
            {
                "join_rules": _or_none(join_rules, "join_rule"),
                "history_visibility": _or_none(
                    history_visibility, "history_visibility"
                ),
                "encryption": _or_none(encryption, "algorithm"),
                "name": _or_none(name, "name"),
                "topic": _or_none(topic, "topic"),
                "avatar": _or_none(avatar, "url"),
                "canonical_alias": _or_none(canonical_alias, "alias"),
            },
        )

        now = self.hs.get_reactor().seconds()

        # quantise time to the nearest bucket
        now = (now // self.stats_bucket_size) * self.stats_bucket_size

        def _fetch_data(txn):
            # Get the current token of the room
            current_token = self._get_max_stream_id_in_current_state_deltas_txn(txn)

            current_state_events = len(current_state_ids)
            joined_members = self._get_user_count_in_room_txn(
                txn, room_id, Membership.JOIN
            )
            invited_members = self._get_user_count_in_room_txn(
                txn, room_id, Membership.INVITE
            )
            left_members = self._get_user_count_in_room_txn(
                txn, room_id, Membership.LEAVE
            )
            banned_members = self._get_user_count_in_room_txn(
                txn, room_id, Membership.BAN
            )
            total_state_events = self._get_total_state_event_counts_txn(txn, room_id)

            self._update_stats_txn(
                txn,
                "room",
                room_id,
                now,
                {
                    "bucket_size": self.stats_bucket_size,
                    "current_state_events": current_state_events,
                    "joined_members": joined_members,
                    "invited_members": invited_members,
                    "left_members": left_members,
                    "banned_members": banned_members,
                    "state_events": total_state_events,
                },
            )
            self._simple_insert_txn(
                txn,
                "room_stats_earliest_token",
                {"room_id": room_id, "token": current_token},
            )

        yield self.runInteraction("update_room_stats", _fetch_data)

        # We've finished a room. Delete it from the table.
        yield self._simple_delete_one(TEMP_TABLE + "_rooms", {"room_id": room_id})
        # Update the remaining counter.
        progress["remaining"] -= 1
        yield self.runInteraction(
            "populate_stats",
            self._background_update_progress_txn,
            "populate_stats_process_rooms",
            progress,
        )

        processed_event_count += event_count

        if processed_event_count > batch_size:
            # Don't process any more rooms, we've hit our batch size.
            defer.returnValue(processed_event_count)

    defer.returnValue(processed_event_count)


# Traceback (TypeError)
2019-05-22 23:12:51,628 - twisted - 242 - ERROR - background_updates-0 - --- Logging error ---
2019-05-22 23:12:51,629 - twisted - 242 - ERROR - background_updates-0 - Traceback (most recent call last):
2019-05-22 23:12:51,629 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 1418, in _inlineCallbacks
2019-05-22 23:12:51,629 - twisted - 242 - ERROR - background_updates-0 -     result = g.send(result)
2019-05-22 23:12:51,629 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/synapse/storage/_base.py", line 527, in runWithConnection
2019-05-22 23:12:51,630 - twisted - 242 - ERROR - background_updates-0 -     defer.returnValue(result)
2019-05-22 23:12:51,630 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 1362, in returnValue
2019-05-22 23:12:51,630 - twisted - 242 - ERROR - background_updates-0 -     raise _DefGen_Return(val)
2019-05-22 23:12:51,630 - twisted - 242 - ERROR - background_updates-0 - twisted.internet.defer._DefGen_Return: [('!redacted', 7), ... snipped ...]
2019-05-22 23:12:51,633 - twisted - 242 - ERROR - background_updates-0 -
2019-05-22 23:12:51,633 - twisted - 242 - ERROR - background_updates-0 - During handling of the above exception, another exception occurred:
2019-05-22 23:12:51,633 - twisted - 242 - ERROR - background_updates-0 -
2019-05-22 23:12:51,633 - twisted - 242 - ERROR - background_updates-0 - Traceback (most recent call last):
2019-05-22 23:12:51,633 - twisted - 242 - ERROR - background_updates-0 -   File "/usr/lib/python3.6/logging/handlers.py", line 71, in emit
2019-05-22 23:12:51,633 - twisted - 242 - ERROR - background_updates-0 -     if self.shouldRollover(record):
2019-05-22 23:12:51,634 - twisted - 242 - ERROR - background_updates-0 -   File "/usr/lib/python3.6/logging/handlers.py", line 187, in shouldRollover
2019-05-22 23:12:51,634 - twisted - 242 - ERROR - background_updates-0 -     msg = "%s\n" % self.format(record)
2019-05-22 23:12:51,634 - twisted - 242 - ERROR - background_updates-0 -   File "/usr/lib/python3.6/logging/__init__.py", line 840, in format
2019-05-22 23:12:51,634 - twisted - 242 - ERROR - background_updates-0 -     return fmt.format(record)
2019-05-22 23:12:51,634 - twisted - 242 - ERROR - background_updates-0 -   File "/usr/lib/python3.6/logging/__init__.py", line 577, in format
2019-05-22 23:12:51,634 - twisted - 242 - ERROR - background_updates-0 -     record.message = record.getMessage()
2019-05-22 23:12:51,635 - twisted - 242 - ERROR - background_updates-0 -   File "/usr/lib/python3.6/logging/__init__.py", line 338, in getMessage
2019-05-22 23:12:51,635 - twisted - 242 - ERROR - background_updates-0 -     msg = msg % self.args
2019-05-22 23:12:51,635 - twisted - 242 - ERROR - background_updates-0 - TypeError: %d format: a number is required, not tuple
2019-05-22 23:12:51,635 - twisted - 242 - ERROR - background_updates-0 - Call stack:
2019-05-22 23:12:51,635 - twisted - 242 - ERROR - background_updates-0 -   File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
2019-05-22 23:12:51,636 - twisted - 242 - ERROR - background_updates-0 -     "__main__", mod_spec)
2019-05-22 23:12:51,636 - twisted - 242 - ERROR - background_updates-0 -   File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
2019-05-22 23:12:51,636 - twisted - 242 - ERROR - background_updates-0 -     exec(code, run_globals)
2019-05-22 23:12:51,636 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/synapse/app/homeserver.py", line 663, in <module>
2019-05-22 23:12:51,636 - twisted - 242 - ERROR - background_updates-0 -     main()
2019-05-22 23:12:51,636 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/synapse/app/homeserver.py", line 659, in main
2019-05-22 23:12:51,637 - twisted - 242 - ERROR - background_updates-0 -     run(hs)
2019-05-22 23:12:51,637 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/synapse/app/homeserver.py", line 650, in run
2019-05-22 23:12:51,637 - twisted - 242 - ERROR - background_updates-0 -     logger=logger,
2019-05-22 23:12:51,637 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/synapse/app/_base.py", line 144, in start_reactor
2019-05-22 23:12:51,637 - twisted - 242 - ERROR - background_updates-0 -     daemon.start()
2019-05-22 23:12:51,637 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/daemonize.py", line 248, in start
2019-05-22 23:12:51,638 - twisted - 242 - ERROR - background_updates-0 -     self.action(*privileged_action_result)
2019-05-22 23:12:51,638 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/synapse/app/_base.py", line 130, in run
2019-05-22 23:12:51,638 - twisted - 242 - ERROR - background_updates-0 -     reactor.run()
2019-05-22 23:12:51,638 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/base.py", line 1267, in run
2019-05-22 23:12:51,638 - twisted - 242 - ERROR - background_updates-0 -     self.mainLoop()
2019-05-22 23:12:51,638 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/base.py", line 1276, in mainLoop
2019-05-22 23:12:51,639 - twisted - 242 - ERROR - background_updates-0 -     self.runUntilCurrent()
2019-05-22 23:12:51,639 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/synapse/metrics/__init__.py", line 347, in f
2019-05-22 23:12:51,639 - twisted - 242 - ERROR - background_updates-0 -     ret = func(*args, **kwargs)
2019-05-22 23:12:51,639 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/base.py", line 875, in runUntilCurrent
2019-05-22 23:12:51,639 - twisted - 242 - ERROR - background_updates-0 -     f(*a, **kw)
2019-05-22 23:12:51,639 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 460, in callback
2019-05-22 23:12:51,639 - twisted - 242 - ERROR - background_updates-0 -     self._startRunCallbacks(result)
2019-05-22 23:12:51,640 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 568, in _startRunCallbacks
2019-05-22 23:12:51,640 - twisted - 242 - ERROR - background_updates-0 -     self._runCallbacks()
2019-05-22 23:12:51,640 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
2019-05-22 23:12:51,640 - twisted - 242 - ERROR - background_updates-0 -     current.result = callback(current.result, *args, **kw)
2019-05-22 23:12:51,640 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 1475, in gotResult
2019-05-22 23:12:51,640 - twisted - 242 - ERROR - background_updates-0 -     _inlineCallbacks(r, g, status)
2019-05-22 23:12:51,640 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 1461, in _inlineCallbacks
2019-05-22 23:12:51,641 - twisted - 242 - ERROR - background_updates-0 -     status.deferred.callback(e.value)
2019-05-22 23:12:51,641 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 460, in callback
2019-05-22 23:12:51,641 - twisted - 242 - ERROR - background_updates-0 -     self._startRunCallbacks(result)
2019-05-22 23:12:51,641 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 568, in _startRunCallbacks
2019-05-22 23:12:51,641 - twisted - 242 - ERROR - background_updates-0 -     self._runCallbacks()
2019-05-22 23:12:51,641 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
2019-05-22 23:12:51,642 - twisted - 242 - ERROR - background_updates-0 -     current.result = callback(current.result, *args, **kw)
2019-05-22 23:12:51,642 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 1475, in gotResult
2019-05-22 23:12:51,642 - twisted - 242 - ERROR - background_updates-0 -     _inlineCallbacks(r, g, status)
2019-05-22 23:12:51,642 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 1461, in _inlineCallbacks
2019-05-22 23:12:51,642 - twisted - 242 - ERROR - background_updates-0 -     status.deferred.callback(e.value)
2019-05-22 23:12:51,642 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 460, in callback
2019-05-22 23:12:51,642 - twisted - 242 - ERROR - background_updates-0 -     self._startRunCallbacks(result)
2019-05-22 23:12:51,643 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 568, in _startRunCallbacks
2019-05-22 23:12:51,643 - twisted - 242 - ERROR - background_updates-0 -     self._runCallbacks()
2019-05-22 23:12:51,643 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
2019-05-22 23:12:51,643 - twisted - 242 - ERROR - background_updates-0 -     current.result = callback(current.result, *args, **kw)
2019-05-22 23:12:51,643 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 1475, in gotResult
2019-05-22 23:12:51,643 - twisted - 242 - ERROR - background_updates-0 -     _inlineCallbacks(r, g, status)
2019-05-22 23:12:51,643 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/twisted/internet/defer.py", line 1418, in _inlineCallbacks
2019-05-22 23:12:51,644 - twisted - 242 - ERROR - background_updates-0 -     result = g.send(result)
2019-05-22 23:12:51,644 - twisted - 242 - ERROR - background_updates-0 -   File "/home/matrix/.synapse/local/lib/python3.6/site-packages/synapse/storage/stats.py", line 172, in _populate_stats_process_rooms
2019-05-22 23:12:51,644 - twisted - 242 - ERROR - background_updates-0 -     (len(rooms_to_work_on), progress["remaining"]),
2019-05-22 23:12:51,644 - twisted - 242 - ERROR - background_updates-0 - Message: 'Processing the next %d rooms of %d remaining'
2019-05-22 23:12:51,644 - twisted - 242 - ERROR - background_updates-0 - Arguments: ((176, 176),)

# Fixed code
def _populate_stats_process_rooms(self, progress, batch_size):
    if not self.stats_enabled:
        yield self._end_background_update("populate_stats_process_rooms")
        defer.returnValue(1)

    # If we don't have progress filed, delete everything.
    if not progress:
        yield self.delete_all_stats()

    def _get_next_batch(txn):
        # Only fetch 250 rooms, so we don't fetch too many at once, even
        # if those 250 rooms have less than batch_size state events.
        sql = """
                SELECT room_id, events FROM %s_rooms
                ORDER BY events DESC
                LIMIT 250
            """ % (TEMP_TABLE,)
        txn.execute(sql)
        rooms_to_work_on = txn.fetchall()

        if not rooms_to_work_on:
            return None

        # Get how many are left to process, so we can give status on how
        # far we are in processing
        txn.execute("SELECT COUNT(*) FROM " + TEMP_TABLE + "_rooms")
        progress["remaining"] = txn.fetchone()[0]

        return rooms_to_work_on

    rooms_to_work_on = yield self.runInteraction(
        "populate_stats_temp_read", _get_next_batch
    )

    # No more rooms -- complete the transaction.
    if not rooms_to_work_on:
        yield self._end_background_update("populate_stats_process_rooms")
        defer.returnValue(1)

    logger.info(
        "Processing the next %d rooms of %d remaining",
        len(rooms_to_work_on),
        progress["remaining"],
    )

    # Number of state events we've processed by going through each room
    processed_event_count = 0

    for room_id, event_count in rooms_to_work_on:
        current_state_ids = yield self.get_current_state_ids(room_id)

        join_rules = yield self.get_event(
            current_state_ids.get((EventTypes.JoinRules, "")), allow_none=True
        )
        history_visibility = yield self.get_event(
            current_state_ids.get((EventTypes.RoomHistoryVisibility, "")),
            allow_none=True,
        )
        encryption = yield self.get_event(
            current_state_ids.get((EventTypes.RoomEncryption, "")), allow_none=True
        )
        name = yield self.get_event(
            current_state_ids.get((EventTypes.Name, "")), allow_none=True
        )
        topic = yield self.get_event(
            current_state_ids.get((EventTypes.Topic, "")), allow_none=True
        )
        avatar = yield self.get_event(
            current_state_ids.get((EventTypes.RoomAvatar, "")), allow_none=True
        )
        canonical_alias = yield self.get_event(
            current_state_ids.get((EventTypes.CanonicalAlias, "")), allow_none=True
        )

        def _or_none(x, arg):
            if x:
                return x.content.get(arg)
            return None

        yield self.update_room_state(
            room_id,
            {
                "join_rules": _or_none(join_rules, "join_rule"),
                "history_visibility": _or_none(
                    history_visibility, "history_visibility"
                ),
                "encryption": _or_none(encryption, "algorithm"),
                "name": _or_none(name, "name"),
                "topic": _or_none(topic, "topic"),
                "avatar": _or_none(avatar, "url"),
                "canonical_alias": _or_none(canonical_alias, "alias"),
            },
        )

        now = self.hs.get_reactor().seconds()

        # quantise time to the nearest bucket
        now = (now // self.stats_bucket_size) * self.stats_bucket_size

        def _fetch_data(txn):
            # Get the current token of the room
            current_token = self._get_max_stream_id_in_current_state_deltas_txn(txn)

            current_state_events = len(current_state_ids)
            joined_members = self._get_user_count_in_room_txn(
                txn, room_id, Membership.JOIN
            )
            invited_members = self._get_user_count_in_room_txn(
                txn, room_id, Membership.INVITE
            )
            left_members = self._get_user_count_in_room_txn(
                txn, room_id, Membership.LEAVE
            )
            banned_members = self._get_user_count_in_room_txn(
                txn, room_id, Membership.BAN
            )
            total_state_events = self._get_total_state_event_counts_txn(txn, room_id)

            self._update_stats_txn(
                txn,
                "room",
                room_id,
                now,
                {
                    "bucket_size": self.stats_bucket_size,
                    "current_state_events": current_state_events,
                    "joined_members": joined_members,
                    "invited_members": invited_members,
                    "left_members": left_members,
                    "banned_members": banned_members,
                    "state_events": total_state_events,
                },
            )
            self._simple_insert_txn(
                txn,
                "room_stats_earliest_token",
                {"room_id": room_id, "token": current_token},
            )

        yield self.runInteraction("update_room_stats", _fetch_data)

        # We've finished a room. Delete it from the table.
        yield self._simple_delete_one(TEMP_TABLE + "_rooms", {"room_id": room_id})
        # Update the remaining counter.
        progress["remaining"] -= 1
        yield self.runInteraction(
            "populate_stats",
            self._background_update_progress_txn,
            "populate_stats_process_rooms",
            progress,
        )

        processed_event_count += event_count

        if processed_event_count > batch_size:
            # Don't process any more rooms, we've hit our batch size.
            defer.returnValue(processed_event_count)

    defer.returnValue(processed_event_count)