Interesting. I've been following this discussion as my experience is that it is *extremely* hard to make an invisible locking mechanism that is to provide both consistency and performance (no lockouts). So far it seems a silver bullet hasn't been found.
Your suggestion is in line with what I would expect from a solution that makes a "best effort" compromise between the multi-tab browsing experience and the burden put on application authors. What if cookies are accessed between beginTransaction() and commitTransaction(), would it make sense to throw for updates with side-effects here as well? (Even though this would not be the case if done outside the transaction.) In some cases it may be helpful to get this "side-effect notification" for cookies as well... Best regards Mike Wilson Chris Jones wrote: > I'd like to propose that HTML5 specify different schemes than a > conceptual global storage mutex to provide consistency guarantees for > localStorage and cookies. > > Cookies would be protected according to Benjamin Smedberg's > post in the > "[whatwg] Storage mutex and cookies can lead to browser deadlock" > thread. Roughly, this proposal would give scripts a > consistent view of > document.cookie until they completed. AIUI this is stronger > consistency > than Google Chrome provides today, and anecdotal evidence > suggests even > their weaker consistency hasn't "broken the web." > > localStorage would be changed in a non-backwards-compatible way. I > believe that web apps can be partitioned into two classes: those that > have planned for running concurrently (single-event-loop or not) in > multiple "browsing contexts", and those that haven't. I > further posit > that the second class would break when run concurrently in multiple > contexts regardless of multiple event loops, and thus > regardless of the > storage mutex. Even in the single-event-loop world, sites > not prepared > to be loaded in multiple tabs can stomp each other's data even though > script execution is atomic. (I wouldn't dare use my bank's > website in > two tabs at the same time in a single-event-loop browser.) In other > words, storage mutex can't help the second class of sites. > > (I also believe that there's a very large, third class of pages that > work "accidentally" when run concurrently in multiple contexts, even > though they don't plan for that. This is likely because they > don't keep > quasi-persistent data on the client side.) > > Based on that, I believe localStorage should be designed with > the first > class of web apps (those that have considered data consistency across > multiple concurrent contexts) in mind, rather than the second > class. Is > a conceptual global storage mutex the best way for, say, gmail to > guarantee consistency of its e-mail/contacts database? I > don't believe > so: I think that a transactional localStorage is preferable. > Transactional localStorage is easier for browser vendors to implement > and should result in better performance for web apps in multi-process > UAs. It's more of a burden on web app authors than the > hidden storage > mutex, but I think the benefits outweigh the cost. > > I propose adding the functions > > window.localStorage.beginTransaction() > window.localStorage.commitTransaction() > or > window.beginTransaction() > window.commitTransaction() > > (The latter might be preferable if we later decide to add > more resources > with transactional semantics.) > > localStorage.getItem(),. setItem(), .removeItem(), and .clear() would > remain specified as they are today. beginTransaction() would do just > that, open a transaction. Calling localStorage.*() outside > of an open > transaction would cause a script exception to be thrown; this would > unfortunately break all current clients of localStorage. > There might be > cleverer ways to mitigate this breakage by a UA pretending not to > support localStorage until a script called beginTransaction(). > > yieldForStorageUpdates() would no longer be meaningful and should be > removed. > > A transaction would successfully "commit", atomically applying its > modifications to localStorage, if localStorage was not > modified between > beginTransaction() and commitTransaction(). Note that a transaction > consisting entirely of getItem() could fail just as those actually > modifying localStorage. If a transaction failed, the UA > would throw a > TransactionFailed exception to script. The UA would be > allowed to throw > this exception at any time between beginTransaction() and > commitTransaction(). > > There are numerous ways to implement transactional semantics. > Single-event-loop UAs could implement beginTransaction() and > commitTransaction() as no-ops. Multi-event-loop UAs could reuse the > global storage mutex if they had already implemented that > (beginTransaction() == lock, commitTransaction() == unlock). > > Some edge cases: > > * calling commitTransaction() without beginTransaction() > would throw > an exception > > * transactions would not be allowed to be nested, even on different > localStorage DBs. E.g. if site A's script begins a transaction on > A.localStorage, and calls into site B's script embedded in an iframe > which begins a transaction on B.localStorage, an exception > would be thrown. > > * transactions *could* be spread across script executions, alert() > dialogs, sync XHR, or anywhere else the current HTML5 spec > requires the > storage mutex be released. Note that UAs wishing to forbid that > behavior could simply throw a TransactionFailed exception where the > storage mutex would have been released in the current spec. Or this > could be made illegal by the spec. > > * it's not clear to me how to handle async XHRs and Worker messages > sent from within a failed transaction. They could be specified to be > sent or not and either behavior implemented easily. My gut tells me > that they *should* be sent regardless. > > Feedback very much desired. > > Cheers, > Chris > > Addendum: I think that a past argument against a > transactional approach > was that scripts can cause side effects during transactions > that can't > be (easily, performantly) rolled back. This is true, and > troubling in > that it deviates from SQL semantics, but because this proposal is > designed for the first class of web apps I don't believe it's a > compelling argument. Further, a script can only corrupt its > browsing-context-local state by mishandling failed > transactions. Using > gmail as a convenient example, if a transaction failed but > gmail wasn't > prepared to handle the failure, that particular gmail instance would > just break. No e-mails or contacts would be corrupted, and the user > could reload gmail and regain full functionality. Servers should > already be prepared to deal with clients behaving unpredictably. >