Repository: qutebrowser/qutebrowser

Issue:
after_load_hook is very expensive
**Version info (see `:version`)**:
v1.5.2

**Does the bug happen if you start with `--temp-basedir`?** (if applicable):
Yes

**Description**
Changing the page URL using `window.location` or the history API blocks for some reason. It makes pages which change the URL frequently perform badly.

**How to reproduce**
<!-- Link to the affected site, or steps to reproduce the issue
(if possible/applicable). -->
```js
setInterval(()=>location.hash=Math.random(), 100)
```


Discussion:
I'm not convinced it is blocking, as opposed to lots of work being triggered as a result of the URL changing. In particular writing out to the autosave file each time. I tried it with with your example and couldn't even scroll the page, I replaced `save_autosave()` with a stub and then it was just janky, instead of essentially frozen. Doing less expensive work on URL changes should be something we keep in mind but this might be made a non-issue by #4087. Could you try reproducing with a basedir in a ramdisk?

toofar writes:

> I'm not convinced it is blocking, as opposed to lots of work being triggered as a result of the URL changing. In particular writing out to the autosave file each time. I tried it with with your example aund couldn't even scroll the page, I replaced `save_autosave()` with a stub and then it was just janky, instead of essentially frozen. Doing less expensive work on URL changes should be something we keep in mind but this might be made a non-issue by #4087. Could you try reproducing with a basedir in a ramdisk?

FWIW, after #4498 is merged, I want to use the same throttle system to
rate-limit yaml encoding on autosave, which is by far the most expensive part
of the on_load hook (and becomes a real nightmare when yaml c extensions are
disabled).

@toofar Well, the page is unable to do any other work while waiting on the location to be set so it seems blocking to me. Something like a file upload even suffers while location is being updated.
@toofar Using `/tmp` as a basedir seems to make it a lot less noticeable

Resolving patch:
diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py
--- a/qutebrowser/misc/sessions.py
+++ b/qutebrowser/misc/sessions.py
@@ -36,6 +36,7 @@
 from qutebrowser.completion.models import miscmodels
 from qutebrowser.mainwindow import mainwindow
 from qutebrowser.qt import sip
+from qutebrowser.misc import throttle
 
 
 _JsonType = typing.MutableMapping[str, typing.Any]
@@ -130,6 +131,8 @@ def __init__(self, base_path, parent=None):
         self._base_path = base_path
         self._last_window_session = None
         self.did_load = False
+        # throttle autosaves to one minute apart
+        self.save_autosave = throttle.Throttle(self._save_autosave, 60 * 1000)
 
     def _get_session_path(self, name, check_exists=False):
         """Get the session path based on a session name or absolute path.
@@ -322,7 +325,7 @@ def save(self, name, last_window=False, load_next_time=False,
             configfiles.state['general']['session'] = name
         return name
 
-    def save_autosave(self):
+    def _save_autosave(self):
         """Save the autosave session."""
         try:
             self.save('_autosave')
@@ -331,6 +334,8 @@ def save_autosave(self):
 
     def delete_autosave(self):
         """Delete the autosave session."""
+        # cancel any in-flight saves
+        self.save_autosave.cancel()
         try:
             self.delete('_autosave')
         except SessionNotFoundError:


Test patch: