admin管理员组文章数量:1022743
tl;dr I noticed inconsistent behaviour between browsers when writing to localStorage at the exact same time.
Requirement: Even when multiple tabs are open, a specific action (POST-request to refresh OAuth session) should be executed only once. Which tab executes the action does not matter. The point in time to do the refresh derives from the expiration time of the session and is the exact same in all tabs.
Approch: All tabs generate a random number, store it and write to localStorage. They then read the localStorage and if both are the same, then the tab is allowed to execute the action.
let tab = Math.random();
localStorage.setItem('tab',tab);
if(JSON.parse(localStorage.getItem('tab')) === tab) {
console.log('aquired lock');
} else {
console.log('did not aquire lock');
}
JSFiddle - In order to test the behaviour you need to open the fiddle in two tabs and then press Run in both. The timeout is calculated to execute at the next full 10-seconds. (Second 0, 10, 20, 30, 40, 50)
Expectation: Tabs A and B set localStorage['tab'] to a random value, on retriving the value only one tab retrieves the same value as it randomly generated and therefore is allowed to execute the action.
Result: Both A and B still retrieve their own generated value.
I added some timeout the let the memory-dust settle:
let tab = Math.random();
localStorage.setItem('tab',tab);
setTimeout(function(){
if(JSON.parse(localStorage.getItem('tab')) === tab) {
console.log('aquired lock');
} else {
console.log('did not aquire lock');
}
}, 1000);
JSFiddle
Result (Firefox): Tab A retrieves value tab B generated, and vice versa. So no tab is allowed to execute the action.
This is where I got a bit spooked. I checked for console-timestamps, which where exactly the same, and the localStorage in the dev-tools, which showed different values in the different tabs. (Even reloading the tab did show different values in different tabs.)
If writing the value later on (e.g. via the console) all tabs update the value accordingly.
Result (Chrome, Edge): Only one tab logs aquired lock
as expected.
Is there any explaination why Firefox can have different values in localStorage per tab?
I already solved the problem by subscribing to the StorageEvent. The tab with the smallest random-number gets to executed the action.
Used browsers:
- Firefox 68.0 and 60.8.0esr (both 64bit)
- Chrome 75.0.3770.142 (64bit)
- Edge 44.18362.1.0
tl;dr I noticed inconsistent behaviour between browsers when writing to localStorage at the exact same time.
Requirement: Even when multiple tabs are open, a specific action (POST-request to refresh OAuth session) should be executed only once. Which tab executes the action does not matter. The point in time to do the refresh derives from the expiration time of the session and is the exact same in all tabs.
Approch: All tabs generate a random number, store it and write to localStorage. They then read the localStorage and if both are the same, then the tab is allowed to execute the action.
let tab = Math.random();
localStorage.setItem('tab',tab);
if(JSON.parse(localStorage.getItem('tab')) === tab) {
console.log('aquired lock');
} else {
console.log('did not aquire lock');
}
JSFiddle - In order to test the behaviour you need to open the fiddle in two tabs and then press Run in both. The timeout is calculated to execute at the next full 10-seconds. (Second 0, 10, 20, 30, 40, 50)
Expectation: Tabs A and B set localStorage['tab'] to a random value, on retriving the value only one tab retrieves the same value as it randomly generated and therefore is allowed to execute the action.
Result: Both A and B still retrieve their own generated value.
I added some timeout the let the memory-dust settle:
let tab = Math.random();
localStorage.setItem('tab',tab);
setTimeout(function(){
if(JSON.parse(localStorage.getItem('tab')) === tab) {
console.log('aquired lock');
} else {
console.log('did not aquire lock');
}
}, 1000);
JSFiddle
Result (Firefox): Tab A retrieves value tab B generated, and vice versa. So no tab is allowed to execute the action.
This is where I got a bit spooked. I checked for console-timestamps, which where exactly the same, and the localStorage in the dev-tools, which showed different values in the different tabs. (Even reloading the tab did show different values in different tabs.)
If writing the value later on (e.g. via the console) all tabs update the value accordingly.
Result (Chrome, Edge): Only one tab logs aquired lock
as expected.
Is there any explaination why Firefox can have different values in localStorage per tab?
I already solved the problem by subscribing to the StorageEvent. The tab with the smallest random-number gets to executed the action.
Used browsers:
- Firefox 68.0 and 60.8.0esr (both 64bit)
- Chrome 75.0.3770.142 (64bit)
- Edge 44.18362.1.0
-
1. You are writing to the same location every time so
"tab"
gets overwritten. 2. Each domain open to browser (one tab or a thousand tabs) has its own Web Storage. So if you really want different values per tab on the same domain you must.setItem()
with a unique key (ex."tab1", "tab2", "tab3"
– zer00ne Commented Jul 18, 2019 at 7:41 -
@zer00ne I want the storage with key
tab
to be overwritten and consistently have the same value across all tabs. (Which works in all browsers but firefox.) – SpazzMarticus Commented Jul 18, 2019 at 7:52
1 Answer
Reset to default 7This is a fairly standard situation when dealing with shared memory. Each thread accessing shared memory is allowed to keep its own copy (a "cache") for performance reasons until/unless some synchronization occurs, at which point the local copy must be reconciled with the shared copy.
The old storage specification talked about acquiring a storage mutex on every storage operation:
Whenever the properties of a
localStorage
attribute'sStorage
object are to be examined, returned, set, or deleted, whether as part of a direct property access, when checking for the presence of a property, during property enumeration, when determining the number of properties present, or as part of the execution of any of the methods or attributes defined on theStorage
interface, the user agent must first obtain the storage mutex.
But that specification has been subsumed into the WHAT-WG "HTML" specification (which is about a lot more than HTML) in §11 ("Web Storage") and the requirement that every operation must acquire a storage mutex has been dropped. (I don't know why, but I would guess for performance reasons.) The current specification says:
Warning: The
localStorage
attribute provides access to shared state. This specification does not define the interaction with other browsing contexts in a multiprocess user agent, and authors are encouraged to assume that there is no locking mechanism.
The specification also doesn't discuss synchronization of storage across browsing contexts. That means implementations are free to optimize.
Looking into it with a modified version of your script, it looks like Firefox optimizes by having a local copy of local storage for each browsing context (tab) which it appears to update based on the storage
event from other contexts (tabs). But if both tabs set the value (generating a storage
event for the other tab) before the storage
event from the other tab is processed, they both get and process the storage
event from the other, updating with that value (the other tab's value), causing the behavior you describe.
Side note: The operation writing to persistent storage (what a third tab would see if you opened it after doing all this) also appears to be asynchronous, and the two tabs are in a race to see which one writes last (a race that is not always won by the last one writing to its local copy!).
This is effectively a large-scale version of what happens with shared memory between threads when there's only loose synchronization between the threads and no locking semantics, which the spec no longer requires.
Chrome would appear to be doing locking or similar.
tl;dr I noticed inconsistent behaviour between browsers when writing to localStorage at the exact same time.
Requirement: Even when multiple tabs are open, a specific action (POST-request to refresh OAuth session) should be executed only once. Which tab executes the action does not matter. The point in time to do the refresh derives from the expiration time of the session and is the exact same in all tabs.
Approch: All tabs generate a random number, store it and write to localStorage. They then read the localStorage and if both are the same, then the tab is allowed to execute the action.
let tab = Math.random();
localStorage.setItem('tab',tab);
if(JSON.parse(localStorage.getItem('tab')) === tab) {
console.log('aquired lock');
} else {
console.log('did not aquire lock');
}
JSFiddle - In order to test the behaviour you need to open the fiddle in two tabs and then press Run in both. The timeout is calculated to execute at the next full 10-seconds. (Second 0, 10, 20, 30, 40, 50)
Expectation: Tabs A and B set localStorage['tab'] to a random value, on retriving the value only one tab retrieves the same value as it randomly generated and therefore is allowed to execute the action.
Result: Both A and B still retrieve their own generated value.
I added some timeout the let the memory-dust settle:
let tab = Math.random();
localStorage.setItem('tab',tab);
setTimeout(function(){
if(JSON.parse(localStorage.getItem('tab')) === tab) {
console.log('aquired lock');
} else {
console.log('did not aquire lock');
}
}, 1000);
JSFiddle
Result (Firefox): Tab A retrieves value tab B generated, and vice versa. So no tab is allowed to execute the action.
This is where I got a bit spooked. I checked for console-timestamps, which where exactly the same, and the localStorage in the dev-tools, which showed different values in the different tabs. (Even reloading the tab did show different values in different tabs.)
If writing the value later on (e.g. via the console) all tabs update the value accordingly.
Result (Chrome, Edge): Only one tab logs aquired lock
as expected.
Is there any explaination why Firefox can have different values in localStorage per tab?
I already solved the problem by subscribing to the StorageEvent. The tab with the smallest random-number gets to executed the action.
Used browsers:
- Firefox 68.0 and 60.8.0esr (both 64bit)
- Chrome 75.0.3770.142 (64bit)
- Edge 44.18362.1.0
tl;dr I noticed inconsistent behaviour between browsers when writing to localStorage at the exact same time.
Requirement: Even when multiple tabs are open, a specific action (POST-request to refresh OAuth session) should be executed only once. Which tab executes the action does not matter. The point in time to do the refresh derives from the expiration time of the session and is the exact same in all tabs.
Approch: All tabs generate a random number, store it and write to localStorage. They then read the localStorage and if both are the same, then the tab is allowed to execute the action.
let tab = Math.random();
localStorage.setItem('tab',tab);
if(JSON.parse(localStorage.getItem('tab')) === tab) {
console.log('aquired lock');
} else {
console.log('did not aquire lock');
}
JSFiddle - In order to test the behaviour you need to open the fiddle in two tabs and then press Run in both. The timeout is calculated to execute at the next full 10-seconds. (Second 0, 10, 20, 30, 40, 50)
Expectation: Tabs A and B set localStorage['tab'] to a random value, on retriving the value only one tab retrieves the same value as it randomly generated and therefore is allowed to execute the action.
Result: Both A and B still retrieve their own generated value.
I added some timeout the let the memory-dust settle:
let tab = Math.random();
localStorage.setItem('tab',tab);
setTimeout(function(){
if(JSON.parse(localStorage.getItem('tab')) === tab) {
console.log('aquired lock');
} else {
console.log('did not aquire lock');
}
}, 1000);
JSFiddle
Result (Firefox): Tab A retrieves value tab B generated, and vice versa. So no tab is allowed to execute the action.
This is where I got a bit spooked. I checked for console-timestamps, which where exactly the same, and the localStorage in the dev-tools, which showed different values in the different tabs. (Even reloading the tab did show different values in different tabs.)
If writing the value later on (e.g. via the console) all tabs update the value accordingly.
Result (Chrome, Edge): Only one tab logs aquired lock
as expected.
Is there any explaination why Firefox can have different values in localStorage per tab?
I already solved the problem by subscribing to the StorageEvent. The tab with the smallest random-number gets to executed the action.
Used browsers:
- Firefox 68.0 and 60.8.0esr (both 64bit)
- Chrome 75.0.3770.142 (64bit)
- Edge 44.18362.1.0
-
1. You are writing to the same location every time so
"tab"
gets overwritten. 2. Each domain open to browser (one tab or a thousand tabs) has its own Web Storage. So if you really want different values per tab on the same domain you must.setItem()
with a unique key (ex."tab1", "tab2", "tab3"
– zer00ne Commented Jul 18, 2019 at 7:41 -
@zer00ne I want the storage with key
tab
to be overwritten and consistently have the same value across all tabs. (Which works in all browsers but firefox.) – SpazzMarticus Commented Jul 18, 2019 at 7:52
1 Answer
Reset to default 7This is a fairly standard situation when dealing with shared memory. Each thread accessing shared memory is allowed to keep its own copy (a "cache") for performance reasons until/unless some synchronization occurs, at which point the local copy must be reconciled with the shared copy.
The old storage specification talked about acquiring a storage mutex on every storage operation:
Whenever the properties of a
localStorage
attribute'sStorage
object are to be examined, returned, set, or deleted, whether as part of a direct property access, when checking for the presence of a property, during property enumeration, when determining the number of properties present, or as part of the execution of any of the methods or attributes defined on theStorage
interface, the user agent must first obtain the storage mutex.
But that specification has been subsumed into the WHAT-WG "HTML" specification (which is about a lot more than HTML) in §11 ("Web Storage") and the requirement that every operation must acquire a storage mutex has been dropped. (I don't know why, but I would guess for performance reasons.) The current specification says:
Warning: The
localStorage
attribute provides access to shared state. This specification does not define the interaction with other browsing contexts in a multiprocess user agent, and authors are encouraged to assume that there is no locking mechanism.
The specification also doesn't discuss synchronization of storage across browsing contexts. That means implementations are free to optimize.
Looking into it with a modified version of your script, it looks like Firefox optimizes by having a local copy of local storage for each browsing context (tab) which it appears to update based on the storage
event from other contexts (tabs). But if both tabs set the value (generating a storage
event for the other tab) before the storage
event from the other tab is processed, they both get and process the storage
event from the other, updating with that value (the other tab's value), causing the behavior you describe.
Side note: The operation writing to persistent storage (what a third tab would see if you opened it after doing all this) also appears to be asynchronous, and the two tabs are in a race to see which one writes last (a race that is not always won by the last one writing to its local copy!).
This is effectively a large-scale version of what happens with shared memory between threads when there's only loose synchronization between the threads and no locking semantics, which the spec no longer requires.
Chrome would appear to be doing locking or similar.
本文标签: javascriptInconsistency when writing synchronous to localStorage from multiple tabsStack Overflow
版权声明:本文标题:javascript - Inconsistency when writing synchronous to localStorage from multiple tabs - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745535028a2154922.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论