Skip to content

Ability to configure cull_idle_timeout with kernelSpec #1342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion jupyter_server/services/kernels/kernelmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,9 @@ async def cull_kernel_if_idle(self, kernel_id):
await ensure_async(self.shutdown_kernel(kernel_id))
return

kernel_spec_metadata = kernel.kernel_spec.metadata
cull_idle_timeout = kernel_spec_metadata.get("cull_idle_timeout", self.cull_idle_timeout)

if hasattr(
kernel, "last_activity"
): # last_activity is monkey-patched, so ensure that has occurred
Expand All @@ -657,7 +660,7 @@ async def cull_kernel_if_idle(self, kernel_id):
dt_now = utcnow()
dt_idle = dt_now - kernel.last_activity
# Compute idle properties
is_idle_time = dt_idle > timedelta(seconds=self.cull_idle_timeout)
is_idle_time = dt_idle > timedelta(seconds=cull_idle_timeout)
is_idle_execute = self.cull_busy or (kernel.execution_state != "busy")
connections = self._kernel_connections.get(kernel_id, 0)
is_idle_connected = self.cull_connected or not connections
Expand Down
39 changes: 39 additions & 0 deletions tests/services/kernels/test_cull.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
CULL_TIMEOUT = 30 if platform.python_implementation() == "PyPy" else 5
CULL_INTERVAL = 1

sample_kernel_json_with_metadata = {
"argv": ["cat", "{connection_file}"],
"display_name": "Test kernel",
"metadata": {"cull_idle_timeout": 0},
}


@pytest.fixture(autouse=True)
def suppress_deprecation_warnings():
Expand All @@ -24,6 +30,21 @@ def suppress_deprecation_warnings():
yield


@pytest.fixture
def jp_kernelspec_with_metadata(jp_data_dir):
"""Configures some sample kernelspecs in the Jupyter data directory."""
kenrel_spec_name = "sample_with_metadata"
sample_kernel_dir = jp_data_dir.joinpath("kernels", kenrel_spec_name)
sample_kernel_dir.mkdir(parents=True)
# Create kernel json file
sample_kernel_file = sample_kernel_dir.joinpath("kernel.json")
kernel_json = sample_kernel_json_with_metadata.copy()
sample_kernel_file.write_text(json.dumps(kernel_json))
# Create resources text
sample_kernel_resources = sample_kernel_dir.joinpath("resource.txt")
sample_kernel_resources.write_text("resource")


@pytest.mark.parametrize(
"jp_server_config",
[
Expand Down Expand Up @@ -73,6 +94,24 @@ async def test_cull_idle(jp_fetch, jp_ws_fetch):
assert culled


async def test_cull_idle_disable(jp_fetch, jp_ws_fetch, jp_kernelspec_with_metadata):
r = await jp_fetch("api", "kernels", method="POST", allow_nonstandard_methods=True)
kernel = json.loads(r.body.decode())
kid = kernel["id"]

# Open a websocket connection.
ws = await jp_ws_fetch("api", "kernels", kid, "channels")

r = await jp_fetch("api", "kernels", kid, method="GET")
model = json.loads(r.body.decode())
assert model["connections"] == 1
culled = await get_cull_status(kid, jp_fetch) # connected, should not be culled
assert not culled
ws.close()
culled = await get_cull_status(kid, jp_fetch) # not connected, should not be culled
assert not culled


# Pending kernels was released in Jupyter Client 7.1
# It is currently broken on Windows (Jan 2022). When fixed, we can remove the Windows check.
# See https://github.com/jupyter-server/jupyter_server/issues/672
Expand Down