Skip to content

Commit 4094644

Browse files
committed
async fixes in kernel usage handler
- handle get_msg being async _or not_ - use async poller to avoid blocking while waiting for usage response
1 parent 827c1f2 commit 4094644

File tree

2 files changed

+15
-21
lines changed

2 files changed

+15
-21
lines changed

jupyter_resource_usage/api.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
import json
22
from concurrent.futures import ThreadPoolExecutor
3+
from inspect import isawaitable
34

45
import psutil
5-
import zmq
6+
import zmq.asyncio
67
from jupyter_client.jsonutil import date_default
78
from jupyter_server.base.handlers import APIHandler
8-
from jupyter_server.utils import url_path_join
99
from packaging import version
1010
from tornado import web
1111
from tornado.concurrent import run_on_executor
1212

13-
try:
14-
# Traitlets >= 4.3.3
15-
from traitlets import Callable
16-
except ImportError:
17-
from .utils import Callable
18-
1913

2014
try:
2115
import ipykernel
@@ -24,8 +18,6 @@
2418
except ImportError:
2519
USAGE_IS_SUPPORTED = False
2620

27-
MAX_RETRIES = 3
28-
2921

3022
class ApiHandler(APIHandler):
3123
executor = ThreadPoolExecutor(max_workers=5)
@@ -113,17 +105,18 @@ async def get(self, matched_part=None, *args, **kwargs):
113105
usage_request = session.msg("usage_request", {})
114106

115107
control_channel.send(usage_request)
116-
poller = zmq.Poller()
108+
poller = zmq.asyncio.Poller()
117109
control_socket = control_channel.socket
118110
poller.register(control_socket, zmq.POLLIN)
119-
for i in range(1, MAX_RETRIES + 1):
120-
timeout_ms = 1000 * i
121-
events = dict(poller.poll(timeout_ms))
122-
if not events:
123-
self.write(json.dumps({}))
124-
break
125-
if control_socket not in events:
126-
continue
127-
res = await client.control_channel.get_msg(timeout=0)
111+
# previous behavior was 3 retries: 1 + 2 + 3 = 6 seconds
112+
timeout_ms = 6_000
113+
events = dict(await poller.poll(timeout_ms))
114+
if control_socket not in events:
115+
self.write(json.dumps({}))
116+
else:
117+
res = client.control_channel.get_msg(timeout=0)
118+
if isawaitable(res):
119+
# control_channel.get_msg may return a Future,
120+
# depending on configured KernelManager class
121+
res = await res
128122
self.write(json.dumps(res, default=date_default))
129-
break

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies = [
3939
"jupyter_server>=1.0",
4040
"prometheus_client",
4141
"psutil~=5.6",
42+
"pyzmq>=19",
4243
]
4344
dynamic = ["version"]
4445

0 commit comments

Comments
 (0)