# Buggy code
def upsert_monthly_active_user_txn(self, txn, user_id):
    """Updates or inserts monthly active user member

    Note that, after calling this method, it will generally be necessary
    to invalidate the caches on user_last_seen_monthly_active and
    get_monthly_active_count. We can't do that here, because we are running
    in a database thread rather than the main thread, and we can't call
    txn.call_after because txn may not be a LoggingTransaction.

    We consciously do not call is_support_txn from this method because it
    is not possible to cache the response. is_support_txn will be false in
    almost all cases, so it seems reasonable to call it only for
    upsert_monthly_active_user and to call is_support_txn manually
    for cases where upsert_monthly_active_user_txn is called directly,
    like _initialise_reserved_users

    In short, don't call this method with support users. (Support users
    should not appear in the MAU stats).

    Args:
        txn (cursor):
        user_id (str): user to add/update

    Returns:
        bool: True if a new entry was created, False if an
        existing one was updated.
    """

    # Am consciously deciding to lock the table on the basis that is ought
    # never be a big table and alternative approaches (batching multiple
    # upserts into a single txn) introduced a lot of extra complexity.
    # See https://github.com/matrix-org/synapse/issues/3854 for more
    is_insert = self.db.simple_upsert_txn(
        txn,
        table="monthly_active_users",
        keyvalues={"user_id": user_id},
        values={"timestamp": int(self._clock.time_msec())},
    )

    return is_insert


# Traceback (exceptions.AttributeError)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]: 2019-02-13 17:56:49,005 - synapse.http.server - 112 - ERROR - PUT-362- Failed handle request via <function _async_render at 0x7ff0363d32a8>: <XForwardedForRequest at 0x7ff02cca4b48 method=u'PUT' uri=u'/_matrix/client/r0/rooms/<<ROOM ID>>/send/m.room.message/163970?user_id=<<USER ID>>&amp;access_token=<redacted>&amp;ts=1477692257000' clientproto=u'HTTP/1.1' site=18095>: Traceback (most recent call last):
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     current.result = callback(current.result, *args, **kw)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1475, in gotResult
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     _inlineCallbacks(r, g, status)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1416, in _inlineCallbacks
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     result = result.throwExceptionIntoGenerator(g)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/python/failure.py", line 491, in throwExceptionIntoGenerator
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     return g.throw(self.type, self.value, self.tb)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]: --- <exception caught here> ---
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/synapse/http/server.py", line 81, in wrapped_request_handler
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     yield h(self, request)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1416, in _inlineCallbacks
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     result = result.throwExceptionIntoGenerator(g)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/python/failure.py", line 491, in throwExceptionIntoGenerator
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     return g.throw(self.type, self.value, self.tb)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/synapse/http/server.py", line 316, in _async_render
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     callback_return = yield callback(request, **kwargs)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1416, in _inlineCallbacks
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     result = result.throwExceptionIntoGenerator(g)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/python/failure.py", line 491, in throwExceptionIntoGenerator
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     return g.throw(self.type, self.value, self.tb)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/synapse/rest/client/v1/room.py", line 217, in on_POST
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     txn_id=txn_id,
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1416, in _inlineCallbacks
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     result = result.throwExceptionIntoGenerator(g)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/python/failure.py", line 491, in throwExceptionIntoGenerator
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     return g.throw(self.type, self.value, self.tb)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/synapse/handlers/message.py", line 488, in create_and_send_nonmember_event
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     txn_id=txn_id
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1416, in _inlineCallbacks
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     result = result.throwExceptionIntoGenerator(g)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/python/failure.py", line 491, in throwExceptionIntoGenerator
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     return g.throw(self.type, self.value, self.tb)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/synapse/handlers/message.py", line 279, in create_event
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     yield self.auth.check_auth_blocking(requester.user.to_string())
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1418, in _inlineCallbacks
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     result = g.send(result)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:   File "/opt/synapse/env/local/lib/python2.7/site-packages/synapse/api/auth.py", line 812, in check_auth_blocking
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]:     timestamp = yield self.store.user_last_seen_monthly_active(user_id)
Feb 13 17:56:49 ip-10-1-2-232 matrix_synapse[3223]: exceptions.AttributeError: 'EventCreatorSlavedStore' object has no attribute 'user_last_seen_monthly_active'

# Fixed code
def upsert_monthly_active_user_txn(self, txn, user_id):
    """Updates or inserts monthly active user member

    We consciously do not call is_support_txn from this method because it
    is not possible to cache the response. is_support_txn will be false in
    almost all cases, so it seems reasonable to call it only for
    upsert_monthly_active_user and to call is_support_txn manually
    for cases where upsert_monthly_active_user_txn is called directly,
    like _initialise_reserved_users

    In short, don't call this method with support users. (Support users
    should not appear in the MAU stats).

    Args:
        txn (cursor):
        user_id (str): user to add/update

    Returns:
        bool: True if a new entry was created, False if an
        existing one was updated.
    """

    # Am consciously deciding to lock the table on the basis that is ought
    # never be a big table and alternative approaches (batching multiple
    # upserts into a single txn) introduced a lot of extra complexity.
    # See https://github.com/matrix-org/synapse/issues/3854 for more
    is_insert = self.db.simple_upsert_txn(
        txn,
        table="monthly_active_users",
        keyvalues={"user_id": user_id},
        values={"timestamp": int(self._clock.time_msec())},
    )

    self._invalidate_cache_and_stream(txn, self.get_monthly_active_count, ())
    self._invalidate_cache_and_stream(
        txn, self.user_last_seen_monthly_active, (user_id,)
    )

    return is_insert