# Buggy code
async def update_last_activity(self):
    """Update User.last_activity timestamps from the proxy"""
    routes = await self.proxy.get_all_routes()
    users_count = 0
    active_users_count = 0
    now = datetime.utcnow()
    for prefix, route in routes.items():
        route_data = route["data"]
        if "user" not in route_data:
            # not a user route, ignore it
            continue
        if "server_name" not in route_data:
            continue
        users_count += 1
        if "last_activity" not in route_data:
            # no last activity data (possibly proxy other than CHP)
            continue
        user = orm.User.find(self.db, route_data["user"])
        if user is None:
            self.log.warning("Found no user for route: %s", route)
            continue
        spawner = user.orm_spawners.get(route_data["server_name"])
        if spawner is None:
            self.log.warning("Found no spawner for route: %s", route)
            continue
        dt = parse_date(route_data["last_activity"])
        if dt.tzinfo:
            # strip timezone info to naïve UTC datetime
            dt = dt.astimezone(timezone.utc).replace(tzinfo=None)

        if user.last_activity:
            user.last_activity = max(user.last_activity, dt)
        else:
            user.last_activity = dt
        if spawner.last_activity:
            spawner.last_activity = max(spawner.last_activity, dt)
        else:
            spawner.last_activity = dt
        # FIXME: Make this configurable duration. 30 minutes for now!
        if (now - user.last_activity).total_seconds() < 30 * 60:
            active_users_count += 1
    self.statsd.gauge("users.running", users_count)
    self.statsd.gauge("users.active", active_users_count)

    self.db.commit()
    await self.proxy.check_routes(self.users, self._service_map, routes)


# Traceback (sqlalchemy.exc.StatementError)
[E 2018-01-19 04:14:19.920 JupyterHub web:1518] Exception in exception handler
Traceback (most recent call last):
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/tornado/web.py", line 1516, in _execute
self._handle_request_exception(e)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/tornado/web.py", line 1570, in _handle_request_exception
self.send_error(500, exc_info=sys.exc_info())
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/tornado/web.py", line 1040, in send_error
self.finish()
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/jupyterhub/handlers/base.py", line 105, in finish
super().finish(*args, **kwargs)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/tornado/web.py", line 993, in finish
self._log()
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/tornado/web.py", line 1540, in _log
self.application.log_request(self)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/tornado/web.py", line 2053, in log_request
self.settings["log_function"](handler)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/jupyterhub/log.py", line 90, in log_request
user = handler.get_current_user()
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/jupyterhub/handlers/base.py", line 229, in get_current_user
return self.get_current_user_cookie()
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/jupyterhub/handlers/base.py", line 222, in get_current_user_cookie
return self._user_for_cookie(self.hub.cookie_name)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/jupyterhub/handlers/base.py", line 206, in _user_for_cookie
u = self.db.query(orm.User).filter(orm.User.cookie_id==cookie_id).first()
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2755, in first
ret = list(self[0:1])
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2547, in __getitem__
return list(res)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2855, in __iter__
return self._execute_and_instances(context)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2878, in _execute_and_instances
result = conn.execute(querycontext.statement, self._params)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 945, in execute
return meth(self, multiparams, params)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/sql/elements.py", line 263, in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement
compiled_sql, distilled_params
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1121, in _execute_context
None, None)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1402, in _handle_dbapi_exception
exc_info
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 203, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb, cause=cause)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 186, in reraise
raise value.with_traceback(tb)
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1114, in _execute_context
conn = self._revalidate_connection()
File "/Users/bhatt/anaconda3/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 424, in _revalidate_connection
"Can't reconnect until invalid "
sqlalchemy.exc.StatementError: (sqlalchemy.exc.InvalidRequestError) Can't reconnect until invalid transaction is rolled back [SQL: 'SELECT users.id AS users_id, users.name AS users_name, users.`admin` AS users_admin, users.last_activity AS users_last_activity, users.cookie_id AS users_cookie_id, users.state AS users_state, users.encrypted_auth_state AS users_encrypted_auth_state \nFROM users \nWHERE users.cookie_id = %(cookie_id_1)s \n LIMIT %(param_1)s'] [parameters: [{}]]

# Fixed code
async def update_last_activity(self):
    """Update User.last_activity timestamps from the proxy"""
    routes = await self.proxy.get_all_routes()
    users_count = 0
    active_users_count = 0
    now = datetime.utcnow()
    for prefix, route in routes.items():
        route_data = route["data"]
        if "user" not in route_data:
            # not a user route, ignore it
            continue
        if "server_name" not in route_data:
            continue
        users_count += 1
        if "last_activity" not in route_data:
            # no last activity data (possibly proxy other than CHP)
            continue
        user = orm.User.find(self.db, route_data["user"])
        if user is None:
            self.log.warning("Found no user for route: %s", route)
            continue
        spawner = user.orm_spawners.get(route_data["server_name"])
        if spawner is None:
            self.log.warning("Found no spawner for route: %s", route)
            continue
        dt = parse_date(route_data["last_activity"])
        if dt.tzinfo:
            # strip timezone info to naïve UTC datetime
            dt = dt.astimezone(timezone.utc).replace(tzinfo=None)

        if user.last_activity:
            user.last_activity = max(user.last_activity, dt)
        else:
            user.last_activity = dt
        if spawner.last_activity:
            spawner.last_activity = max(spawner.last_activity, dt)
        else:
            spawner.last_activity = dt
        # FIXME: Make this configurable duration. 30 minutes for now!
        if (now - user.last_activity).total_seconds() < 30 * 60:
            active_users_count += 1
    self.statsd.gauge("users.running", users_count)
    self.statsd.gauge("users.active", active_users_count)

    try:
        self.db.commit()
    except SQLAlchemyError:
        self.log.exception("Rolling back session due to database error")
        self.db.rollback()
        return

    await self.proxy.check_routes(self.users, self._service_map, routes)