]> git.proxmox.com Git - proxmox-widget-toolkit.git/commitdiff
fix #4421: ui: guard setProxy against races of slow vs fast requests
authorFriedrich Weber <f.weber@proxmox.com>
Mon, 6 Mar 2023 14:03:14 +0000 (15:03 +0100)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 8 Mar 2023 06:42:11 +0000 (07:42 +0100)
Some UI components use `Ext.data.Store.setProxy` to change their
associated API endpoint URL in reaction to user input. One example is
`BackupView`, which calls `setProxy` when the user switches from
listing backups on storage A to listing backups on storage B. However,
if A is slow, the UI may receive the response for A *after* the
response for B. It will then display the contents of A as if they were
the contents of B, resulting in a UI inconsistency.

The reason is that `Ext.data.Store` still processes the slow response
for A, even though it is obsolete. This patch overrides the
responsible callback of `Ext.data.Store` to only process responses
belonging to the currently active proxy object. This should rule out
similar race conditions in all components that use the `setProxy` API.
In the above example, the patch results in the response for A being
ignored.

Ignored responses are logged to the browser console.

Note that this patch only concerns components that use `setProxy` for
changing API endpoints. Other components (e.g. those using
`proxy.setURL` for the same purpose) may be open to similar race
conditions.

Link: https://lists.proxmox.com/pipermail/pve-devel/2023-March/056062.html
Signed-off-by: Friedrich Weber <f.weber@proxmox.com>
src/Utils.js

index f55b9a5371d3ca8948a5f65b12eabed1515f8554..8a974870540b6d5e428267a2f59c27da62b04170 100644 (file)
@@ -1451,3 +1451,18 @@ Ext.define('Proxmox.Async', {
        return new Promise((resolve, _reject) => setTimeout(resolve, millis));
     },
 });
+
+Ext.override(Ext.data.Store, {
+    // If the store's proxy is changed while it is waiting for an AJAX
+    // response, `onProxyLoad` will still be called for the outdated response.
+    // To avoid displaying inconsistent information, only process responses
+    // belonging to the current proxy.
+    onProxyLoad: function(operation) {
+       let me = this;
+       if (operation.getProxy() === me.getProxy()) {
+           me.callParent(arguments);
+       } else {
+           console.log(`ignored outdated response: ${operation.getRequest().getUrl()}`);
+       }
+    },
+});