mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-08 02:24:08 +00:00
Merge pull request #1290 from yuvipanda/concurrent_spawn_limit
Add support for limiting the number of concurrent spawns
This commit is contained in:
@@ -534,11 +534,11 @@ class JupyterHub(Application):
|
|||||||
@default('authenticator')
|
@default('authenticator')
|
||||||
def _authenticator_default(self):
|
def _authenticator_default(self):
|
||||||
return self.authenticator_class(parent=self, db=self.db)
|
return self.authenticator_class(parent=self, db=self.db)
|
||||||
|
|
||||||
allow_named_servers = Bool(False,
|
allow_named_servers = Bool(False,
|
||||||
help="Allow named single-user servers per user"
|
help="Allow named single-user servers per user"
|
||||||
).tag(config=True)
|
).tag(config=True)
|
||||||
|
|
||||||
# class for spawning single-user servers
|
# class for spawning single-user servers
|
||||||
spawner_class = Type(LocalProcessSpawner, Spawner,
|
spawner_class = Type(LocalProcessSpawner, Spawner,
|
||||||
help="""The class to use for spawning single-user servers.
|
help="""The class to use for spawning single-user servers.
|
||||||
@@ -547,6 +547,18 @@ class JupyterHub(Application):
|
|||||||
"""
|
"""
|
||||||
).tag(config=True)
|
).tag(config=True)
|
||||||
|
|
||||||
|
concurrent_spawn_limit = Integer(
|
||||||
|
0,
|
||||||
|
help="""
|
||||||
|
Maximum number of concurrent users that can be spawning at a time.
|
||||||
|
|
||||||
|
If more than this many users attempt to spawn at a time, their
|
||||||
|
request is rejected with a 429 error asking them to try again.
|
||||||
|
|
||||||
|
If set to 0, no concurrent_spawn_limit is enforced.
|
||||||
|
"""
|
||||||
|
).tag(config=True)
|
||||||
|
|
||||||
db_url = Unicode('sqlite:///jupyterhub.sqlite',
|
db_url = Unicode('sqlite:///jupyterhub.sqlite',
|
||||||
help="url for the database. e.g. `sqlite:///jupyterhub.sqlite`"
|
help="url for the database. e.g. `sqlite:///jupyterhub.sqlite`"
|
||||||
).tag(config=True)
|
).tag(config=True)
|
||||||
@@ -1257,6 +1269,7 @@ class JupyterHub(Application):
|
|||||||
statsd=self.statsd,
|
statsd=self.statsd,
|
||||||
allow_named_servers=self.allow_named_servers,
|
allow_named_servers=self.allow_named_servers,
|
||||||
oauth_provider=self.oauth_provider,
|
oauth_provider=self.oauth_provider,
|
||||||
|
concurrent_spawn_limit=self.concurrent_spawn_limit,
|
||||||
)
|
)
|
||||||
# allow configured settings to have priority
|
# allow configured settings to have priority
|
||||||
settings.update(self.tornado_settings)
|
settings.update(self.tornado_settings)
|
||||||
|
@@ -367,6 +367,19 @@ class BaseHandler(RequestHandler):
|
|||||||
def spawn_single_user(self, user, server_name='', options=None):
|
def spawn_single_user(self, user, server_name='', options=None):
|
||||||
if server_name in user.spawners and user.spawners[server_name]._spawn_pending:
|
if server_name in user.spawners and user.spawners[server_name]._spawn_pending:
|
||||||
raise RuntimeError("Spawn already pending for: %s" % user.name)
|
raise RuntimeError("Spawn already pending for: %s" % user.name)
|
||||||
|
|
||||||
|
concurrent_spawn_limit = self.settings['concurrent_spawn_limit']
|
||||||
|
if concurrent_spawn_limit and self.settings.get('_spawn_pending_count', 0) > concurrent_spawn_limit:
|
||||||
|
self.log.info(
|
||||||
|
'More than %s pending spawns, throttling',
|
||||||
|
self.settings['concurrent_spawn_limit']
|
||||||
|
)
|
||||||
|
raise web.HTTPError(
|
||||||
|
429,
|
||||||
|
"User startup rate limit exceeded. Try to start again in a few minutes.")
|
||||||
|
|
||||||
|
# FIXME: Move this out of settings, since this isn't really a setting
|
||||||
|
self.settings['_spawn_pending_count'] = self.settings.get('_spawn_pending_count', 0) + 1
|
||||||
tic = IOLoop.current().time()
|
tic = IOLoop.current().time()
|
||||||
user_server_name = user.name
|
user_server_name = user.name
|
||||||
if server_name:
|
if server_name:
|
||||||
@@ -403,6 +416,7 @@ class BaseHandler(RequestHandler):
|
|||||||
spawner.add_poll_callback(self.user_stopped, user)
|
spawner.add_poll_callback(self.user_stopped, user)
|
||||||
finally:
|
finally:
|
||||||
spawner._proxy_pending = False
|
spawner._proxy_pending = False
|
||||||
|
self.settings['_spawn_pending_count'] -= 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield gen.with_timeout(timedelta(seconds=self.slow_spawn_timeout), f)
|
yield gen.with_timeout(timedelta(seconds=self.slow_spawn_timeout), f)
|
||||||
|
Reference in New Issue
Block a user