Skip to content

Commit

Permalink
Fix the activity and execution-state tracking for kernels.
Browse files Browse the repository at this point in the history
This commit changes the way Jupyter server tracks both activity and
execution state so that both of those are based solely on user actions
rather than also incorporating control messages and other channels.

This is important for both correctly tracking the state of the kernel,
and also for culling kernels.

Previously, the last activity timestamp was updated on every message
on the iopub channel regardless of the type of message, and execution
state was based on every `status` message on that channel regardless
of what other message the `status` was in response to.

That behavior causes incorrect behavior for both kernel culling and
kernel status. In particular, it can result in both kernels being culled
too early and too late (depending on what the user is doing).

For example, if a user ran a long running code cell, and then switched
tabs within the JupyterLab UI, then the JupyterLab UI would send a
control message to the kernel and the kernel would respond with a status
message referencing that control message as its parent. As a result of
that, the Jupyter server would update its execution state to be `idle`
even though the long running code cell was still executing. This could
cause the kernel to be culled too soon.

Alternatively, if the user was not running anything and just switched
tabs within the JupyterLab UI, then the last activity timestamp would
be updated even though the user didn't run any code cells. That would
cause the kernel to be culled too late.

This change fixes both of those scenarios by making sure that it is
only user actions (and kernel messages in response to those user
actions) that cause the execution state and last activity timestamps
to be updated.

This initial commit contains just the code change so that I can
solicit feedback on this proposed change early.

The core of the change will have to be new tests, which will come
in a subsequent commit as part of the same pull request.
  • Loading branch information
ojarjur committed Nov 17, 2023
1 parent 8ed8b33 commit 67255a9
Showing 1 changed file with 26 additions and 7 deletions.
33 changes: 26 additions & 7 deletions jupyter_server/services/kernels/kernelmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,17 +549,36 @@ def start_watching_activity(self, kernel_id):
key=kernel.session.key,
)

_user_activities = [
"complete_request",
"execute_input",
"execute_reply",
"execute_request",
"inspect_request",
]

def record_activity(msg_list):
"""Record an IOPub message arriving from a kernel"""
self.last_kernel_activity = kernel.last_activity = utcnow()

idents, fed_msg_list = session.feed_identities(msg_list)
if kernel.execution_state == "starting":
# Starting is the status for a kernel process that has not come up yet.
#
# If we've received a 0mq message from the kernel, then we know it is
# no longer starting and should update the execution state accordingly.
kernel.execution_state = "idle"

_, fed_msg_list = session.feed_identities(msg_list)
msg = session.deserialize(fed_msg_list, content=False)

msg_type = msg["header"]["msg_type"]
if msg_type == "status":
msg_type = msg.get("header", {}).get("msg_type", "")
parent_msg_type = msg.get("parent_header", {}).get("msg_type", "")
if (
(msg_type in _user_activities)
or (parent_msg_type in _user_activities)
or kernel.execution_state == "busy"
):
self.last_kernel_activity = kernel.last_activity = utcnow()
if msg_type == "status" and parent_msg_type in _user_activities:
msg = session.deserialize(fed_msg_list)
kernel.execution_state = msg["content"]["execution_state"]
kernel.execution_state = msg.get("content", {}).get("execution_state", "")
self.log.debug(
"activity on %s: %s (%s)",
kernel_id,
Expand Down

0 comments on commit 67255a9

Please sign in to comment.