Merge pull request #1221 from minrk/proxy-race

add User.proxy_pending
This commit is contained in:
Yuvi Panda
2017-07-14 19:20:16 -07:00
committed by GitHub
3 changed files with 34 additions and 16 deletions

View File

@@ -321,6 +321,7 @@ class BaseHandler(RequestHandler):
tic = IOLoop.current().time() tic = IOLoop.current().time()
f = user.spawn(options) f = user.spawn(options)
user.proxy_pending = True
@gen.coroutine @gen.coroutine
def finish_user_spawn(f=None): def finish_user_spawn(f=None):
@@ -335,8 +336,16 @@ class BaseHandler(RequestHandler):
toc = IOLoop.current().time() toc = IOLoop.current().time()
self.log.info("User %s server took %.3f seconds to start", user.name, toc-tic) self.log.info("User %s server took %.3f seconds to start", user.name, toc-tic)
self.statsd.timing('spawner.success', (toc - tic) * 1000) self.statsd.timing('spawner.success', (toc - tic) * 1000)
try:
yield self.proxy.add_user(user) yield self.proxy.add_user(user)
except Exception:
self.log.exception("Failed to add user %s to proxy!", user)
self.log.error("Stopping user %s to avoid inconsistent state")
yield user.stop()
else:
user.spawner.add_poll_callback(self.user_stopped, user) user.spawner.add_poll_callback(self.user_stopped, user)
finally:
user.proxy_pending = False
try: try:
yield gen.with_timeout(timedelta(seconds=self.slow_spawn_timeout), f) yield gen.with_timeout(timedelta(seconds=self.slow_spawn_timeout), f)
@@ -532,7 +541,7 @@ class UserSpawnHandler(BaseHandler):
# logged in as correct user, spawn the server # logged in as correct user, spawn the server
if current_user.spawner: if current_user.spawner:
if current_user.spawn_pending: if current_user.spawn_pending or current_user.proxy_pending:
# spawn has started, but not finished # spawn has started, but not finished
self.statsd.incr('redirects.user_spawn_pending', 1) self.statsd.incr('redirects.user_spawn_pending', 1)
html = self.render_template("spawn_pending.html", user=current_user) html = self.render_template("spawn_pending.html", user=current_user)

View File

@@ -4,9 +4,7 @@
# Copyright (c) Jupyter Development Team. # Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License. # Distributed under the terms of the Modified BSD License.
from distutils.version import LooseVersion as V
import os import os
import re
from textwrap import dedent from textwrap import dedent
from urllib.parse import urlparse from urllib.parse import urlparse
@@ -349,10 +347,17 @@ class SingleUserNotebookApp(NotebookApp):
- check version and warn on sufficient mismatch - check version and warn on sufficient mismatch
""" """
client = AsyncHTTPClient() client = AsyncHTTPClient()
RETRIES = 5
for i in range(1, RETRIES+1):
try: try:
resp = yield client.fetch(self.hub_api_url) resp = yield client.fetch(self.hub_api_url)
except Exception: except Exception:
self.log.exception("Failed to connect to my Hub at %s. Is it running?", self.hub_api_url) self.log.exception("Failed to connect to my Hub at %s (attempt %i/%i). Is it running?",
self.hub_api_url, i, RETRIES)
yield gen.sleep(min(2**i, 16))
else:
break
else:
self.exit(1) self.exit(1)
hub_version = resp.headers.get('X-JupyterHub-Version') hub_version = resp.headers.get('X-JupyterHub-Version')

View File

@@ -110,6 +110,7 @@ class User(HasTraits):
spawner = None spawner = None
spawn_pending = False spawn_pending = False
stop_pending = False stop_pending = False
proxy_pending = False
waiting_for_response = False waiting_for_response = False
@property @property
@@ -158,8 +159,8 @@ class User(HasTraits):
@property # FIX-ME CHECK IF STILL NEEDED @property # FIX-ME CHECK IF STILL NEEDED
def running(self): def running(self):
"""property for whether a user has a running server""" """property for whether a user has a fully running, accessible server"""
if self.spawn_pending or self.stop_pending: if self.spawn_pending or self.stop_pending or self.proxy_pending:
return False # server is not running if spawn or stop is still pending return False # server is not running if spawn or stop is still pending
if self.server is None: if self.server is None:
return False return False
@@ -392,10 +393,13 @@ class User(HasTraits):
self.db.delete(orm_token) self.db.delete(orm_token)
self.db.commit() self.db.commit()
finally: finally:
self.stop_pending = False
# trigger post-spawner hook on authenticator # trigger post-spawner hook on authenticator
auth = spawner.authenticator auth = spawner.authenticator
try:
if auth: if auth:
yield gen.maybe_future( yield gen.maybe_future(
auth.post_spawn_stop(self, spawner) auth.post_spawn_stop(self, spawner)
) )
except Exception:
self.log.exception("Error in Authenticator.post_spawn_stop for %s", self)
self.stop_pending = False