From 1e21e00e1ae8c5d3b403f86740b3c4e8271fe784 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 27 Apr 2016 14:26:43 +0200 Subject: [PATCH 1/2] return status from poll_and_notify allows calling it directly --- jupyterhub/spawner.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 07f59c93..62c2ce41 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -15,7 +15,7 @@ from subprocess import Popen from tempfile import mkdtemp from tornado import gen -from tornado.ioloop import IOLoop, PeriodicCallback +from tornado.ioloop import PeriodicCallback from traitlets.config import LoggingConfigurable from traitlets import ( @@ -335,15 +335,17 @@ class Spawner(LoggingConfigurable): self.stop_polling() - add_callback = IOLoop.current().add_callback for callback in self._callbacks: - add_callback(callback) + try: + yield gen.maybe_future(callback()) + except Exception: + self.log.exception("Unhandled error in poll callback for %s", self) + return status death_interval = Float(0.1) @gen.coroutine def wait_for_death(self, timeout=10): """wait for the process to die, up to timeout seconds""" - loop = IOLoop.current() for i in range(int(timeout / self.death_interval)): status = yield self.poll() if status is not None: From 7528fb7d9b188be6c22ba721926390cf609bda10 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 27 Apr 2016 14:28:07 +0200 Subject: [PATCH 2/2] notice dead servers more often call poll_and_notify to ensure triggering of dead-server events in a few places: - `/hub/home` page view - user start and stop API endpoints This should avoid the failure to stop a server that's died uncleanly because the server hasn't noticed yet --- jupyterhub/apihandlers/users.py | 8 +++++--- jupyterhub/handlers/pages.py | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/jupyterhub/apihandlers/users.py b/jupyterhub/apihandlers/users.py index b6fbd67c..97b689cb 100644 --- a/jupyterhub/apihandlers/users.py +++ b/jupyterhub/apihandlers/users.py @@ -161,8 +161,9 @@ class UserServerAPIHandler(APIHandler): @admin_or_self def post(self, name): user = self.find_user(name) - if user.spawner: - state = yield user.spawner.poll() + if user.running: + # include notify, so that a server that died is noticed immediately + state = yield user.spawner.poll_and_notify() if state is None: raise web.HTTPError(400, "%s's server is already running" % name) @@ -180,7 +181,8 @@ class UserServerAPIHandler(APIHandler): return if not user.running: raise web.HTTPError(400, "%s's server is not running" % name) - status = yield user.spawner.poll() + # include notify, so that a server that died is noticed immediately + status = yield user.spawner.poll_and_notify() if status is not None: raise web.HTTPError(400, "%s's server is not running" % name) yield self.stop_single_user(user) diff --git a/jupyterhub/handlers/pages.py b/jupyterhub/handlers/pages.py index 65ba148d..1861f9ab 100644 --- a/jupyterhub/handlers/pages.py +++ b/jupyterhub/handlers/pages.py @@ -41,9 +41,14 @@ class HomeHandler(BaseHandler): """Render the user's home page.""" @web.authenticated + @gen.coroutine def get(self): + user = self.get_current_user() + if user.running: + # trigger poll_and_notify event in case of a server that died + yield user.spawner.poll_and_notify() html = self.render_template('home.html', - user=self.get_current_user(), + user=user, ) self.finish(html)