-
Notifications
You must be signed in to change notification settings - Fork 504
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
Send kernel shutdown request manually on beforeunload #612
Send kernel shutdown request manually on beforeunload #612
Conversation
@@ -40,7 +40,9 @@ require(['static/voila'], function(voila) { | |||
async function init() { | |||
// it seems if we attach this to early, it will not be called | |||
window.addEventListener('beforeunload', function (e) { | |||
kernel.shutdown(); | |||
const xhttp = new XMLHttpRequest(); | |||
xhttp.open("DELETE", `/api/kernels/${kernel.id}`, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that if end up deciding that this is the best way, we would need to ensure we get/set all the appropriate headers and cookies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which is why using sendBeacon
instead sounds interesting 👍 (linking to #528 for more context)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are more things than just cookies though, i.e. a bunch of headers:
https://github.com/jupyterlab/jupyterlab/blob/64425baacfba677b75efaf05cfb825a82264230d/packages/services/src/serverconnection.ts#L236-L282
How many of those are needed for the DELETE request is a bit unknown though.
I think it might be possible to do a workaround by using a ServerConnection with our own settings that override fetch
such that it instead uses a sync XHTTPRequest? It would require "translating" the request first though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are more things than just cookies though, i.e. a bunch of headers:
Indeed. And these are not picked up by sendBeacon
.
Experimented a bit with diff --git a/share/jupyter/voila/templates/default/static/main.js b/share/jupyter/voila/templates/default/static/main.js
index ae5baef..2e1ce4e 100644
--- a/share/jupyter/voila/templates/default/static/main.js
+++ b/share/jupyter/voila/templates/default/static/main.js
@@ -40,9 +40,7 @@ require(['static/voila'], function(voila) {
async function init() {
// it seems if we attach this to early, it will not be called
window.addEventListener('beforeunload', function (e) {
- const xhttp = new XMLHttpRequest();
- xhttp.open("DELETE", `/api/kernels/${kernel.id}`, false);
- xhttp.send();
+ window.navigator.sendBeacon('/api/voila/shutdown/' + kernel.id);
kernel.dispose();
});
await widgetManager.build_widgets();
diff --git a/voila/app.py b/voila/app.py
index 17d9f33..067165d 100644
--- a/voila/app.py
+++ b/voila/app.py
@@ -55,6 +55,7 @@ from ipython_genutils.py3compat import getcwd
from .paths import ROOT, STATIC_ROOT, collect_template_paths
from .handler import VoilaHandler
+from .stophandler import VoilaStopHandler
from .treehandler import VoilaTreeHandler
from ._version import __version__
from .static_file_handler import MultiStaticFileHandler, WhiteListFileHandler
@@ -505,6 +506,9 @@ class Voila(Application):
}),
])
+ handlers.append(
+ (url_path_join(self.server_url, r'/api/voila/shutdown/(.*)'), VoilaStopHandler)
+ )
self.app.add_handlers('.*$', handlers)
self.listen()
diff --git a/voila/stophandler.py b/voila/stophandler.py
new file mode 100644
index 0000000..b8a677d
--- /dev/null
+++ b/voila/stophandler.py
@@ -0,0 +1,10 @@
+import tornado
+
+from jupyter_server.base.handlers import APIHandler
+
+class VoilaStopHandler(APIHandler):
+ @tornado.web.authenticated
+ async def post(self, kernel_id):
+ await self.kernel_manager.shutdown_kernel(kernel_id)
+ self.set_status(204)
+ self.finish() Although this seems to be fine in voila standalone locally, we are indeed missing the authorization headers (when uses as a server extension for example). |
2ea00a6 and deda754 seem to improve things slightly, mostly in the case of the preview extension. Before: After: However it doesn't prevent a user to hit refresh very quickly (#55). In that case not all the requests make it and kernels are not shut down. It also doesn't seem to be effective when the preview panel is closed. |
@vidartf let me know what you think if you get to try it locally. It should already be a slight improvement. Although a more robust fix would probably be to detect when a connection is closed on the server instead, and shut down the corresponding kernel (or at least make that behavior configurable). |
Is this something we'd like to test with say puppeteer? Also, I think long term we should upstream this into jupyterlab right? |
Culling set to 0.01 minutes? |
It would be very interesting to check how this is handled with puppeteer. Whether the request is correctly fired or not.
You mean the option to send requests synchronously?
That could work. Otherwise wouldn't it be possible to know on the server when a client has disconnected? Because this should correspond to a websocket connection closed. |
While I believe there might be a technical solution possible here, I don't know anything about how hard it would be to code it and get a PR accepted.
This would cause a network glitch (the train driving through a tunnel) to kill your kernel, so probably not ideal, but yes, having an aggressive culling time seems to be the current best option. Regarding the most robust solution, I saw someone recommend using a fetch request with the keepalive flag set to true. I'm reading more on that now. |
Regarding the keepalive flag, I think this would be the most ideal thing for us (it was designed for our use case, and it is often what |
I think the current solution is an improvement (not a total fix though). I'd be happy to merge this, until we find a better solution, possibly in the jupyterlab org/repo? |
deda754
to
eeb4098
Compare
Sounds good 👍 |
eeb4098
to
074618f
Compare
Benchmark reportThe execution time (in milliseconds) are grouped by test file, test type and browser. Results table
❗ Test metadata have changed--- /dev/fd/63 2021-09-16 14:03:16.549200147 +0000
+++ /dev/fd/62 2021-09-16 14:03:16.549200147 +0000
@@ -8,27 +8,27 @@
},
"systemInformation": {
"cpu": {
- "brand": "Xeon® Platinum 8171M",
+ "brand": "Xeon® E5-2673 v3",
"cache": {
"l1d": 65536,
"l1i": 65536,
- "l2": 2097152,
- "l3": 36700160
+ "l2": 524288,
+ "l3": 31457280
},
"cores": 2,
"family": "6",
- "flags": "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti fsgsbase bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt avx512cd avx512bw avx512vl xsaveopt xsavec xsaves md_clear",
+ "flags": "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti fsgsbase bmi1 avx2 smep bmi2 erms invpcid xsaveopt md_clear",
"governor": "",
"manufacturer": "Intel®",
- "model": "85",
+ "model": "63",
"physicalCores": 2,
"processors": 1,
"revision": "",
"socket": "",
- "speed": 2.6,
+ "speed": 2.4,
"speedMax": null,
"speedMin": null,
- "stepping": "4",
+ "stepping": "2",
"vendor": "GenuineIntel",
"virtualization": false,
"voltage": ""
@@ -42,11 +42,11 @@
"codename": "Focal Fossa",
"codepage": "UTF-8",
"distro": "Ubuntu",
- "kernel": "5.8.0-1041-azure",
+ "kernel": "5.8.0-1040-azure",
"logofile": "ubuntu",
"platform": "linux",
"release": "20.04.3 LTS",
- "serial": "faac5b9e00cf4ae3b2d2c013e98d933a",
+ "serial": "cfc067bfcb844f35865e279a1b0e66c5",
"servicepack": "",
"uefi": false
} |
To test this branch on Binder: https://mybinder.org/v2/gh/jtpio/voila/shutdown-page-closed?urlpath=voila/tree |
cc @maartenbreddels
This manually sends a
DELETE /api/kernels/<kernel-id>
with async = falsehttps://www.w3schools.com/js/js_ajax_http_send.asp
Related: #528 #55