- From: Israel Hilerio <israelh@microsoft.com>
- Date: Fri, 14 Oct 2011 20:51:26 +0000
- To: "Jonas Sicking (jonas@sicking.cc)" <jonas@sicking.cc>
- CC: "public-webapps@w3.org" <public-webapps@w3.org>, Jim Wordelman <jaword@microsoft.com>, Adam Herchenroether <aherchen@microsoft.com>, "Victor Ngo" <vicngo@microsoft.com>
On Friday, October 07, 2011 4:35 PM, Israel Hilerio wrote: > On Friday, October 07, 2011 2:52 PM, Jonas Sicking wrote: > > Hi All, > > > > There is one edge case regarding transaction scheduling that we'd like > > to get clarified. > > > > As the spec is written, it's clear what the following code should do: > > > > trans1 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > > trans1.objectStore("foo").put("value 1", "mykey"); > > trans2 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > > trans2.objectStore("foo").put("value 2", "mykey"); > > > > In this example it's clear that the implementation should first run > > trans1 which will put the value "value 1" in object store "foo" at key > > "mykey". The implementation should then run trans2 which will write > > overwrite the same value with "value 2". The end result is that "value > > 2" is the value that lives in the object store. > > > > Note that in this case it's not at all ambiguous which transaction runs first. > > Since the two transactions have overlapping scope, trans2 won't even > > start until trans1 is committed. Even if we made the code something like: > > > > trans1 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > > trans1.objectStore("foo").put("value 1", "mykey"); > > trans2 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > > trans2.objectStore("foo").put("value 2", "mykey"); > > trans1.objectStore("foo").put("value 3", "mykey"); > > > > we'd get the same result. Both put requests placed against trans1 will > > run first while trans2 is waiting for trans1 to commit before it > > begins running since they have overlapping scopes. > > > > However, consider the following example: > > trans1 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > > trans2 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > > trans2.objectStore("foo").put("value 2", "mykey"); > > trans1.objectStore("foo").put("value 1", "mykey"); > > > > In this case, while trans1 is created first, no requests are placed > > against it, and so no database operations are started. The first > > database operation that is requested is one placed against trans2. In > > the firefox implementation, this makes trans2 run before trans1. I.e. > > we schedule transactions when the first request is placed against > > them, and not when the IDBDatabase.transaction() function returns. > > > > The advantage of firefox approach is obvious in code like this: > > > > someElement.onclick = function() { > > trans1 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > > ... > > trans2 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > > trans2.objectStore.put("some value", "mykey"); > > callExpensiveFunction(); > > } > > > > In this example no requests are placed against trans1. However since > > trans1 is supposed to run before trans2 does, we can't send off any > > work to the database at the time when the .put call happens since we > > don't yet know if there will be requests placed against trans1. Only > > once we return to the event loop at the end of the onclick handler will > trans1 be "committed" > > and the requests in trans2 can be sent to the database. > > > > However, the downside with firefox approach is that it's harder for > > applications to control which order transactions are run. Consider for > > example a program is parsing a big hunk of binary data. Before > > parsing, the program starts two transactions, one READ_WRITE and one > > READ_ONLY. As the binary data is interpreted, the program issues write > > requests against the READ_WRITE transactions and read requests against > > the READ_ONLY transaction. The idea being that the read requests will > > always run after the write requests to read from database after all > > the parsed data has been written. In this setup the firefox approach > > isn't as good since it's less predictable which transaction will run > > first as it might depend on the binary data being parsed. Of course, > > you could force the writing transaction to run first by placing a request > against it after it has been created. > > > > I am however not able to think of any concrete examples of the above > > binary data structure that would require this setup. > > > > So the question is, which solution do you think we should go with. One > > thing to remember is that there is a very small difference between the > > two approaches here. It only makes a difference in edge cases. The > > edge case being that a transaction is created, but no requests are > > placed against it until another transaction, with overlapping scope, is > created. > > > > Firefox approach has strictly better performance in this edge case. > > However it could also have somewhat surprising results. > > > > I personally don't feel strongly either way. I also think it's rare to > > make a difference one way or another as it'll be rare for people to hit this > edge case. > > > > But we should spell things out clearly in the spec which approach is > > the conforming one. > > > > / Jonas > > > > In IE, the transaction that is first created locks the object stores associated > with it. > Therefore in the scenario outlined by Jonas: > > trans1 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > trans2 = db.transaction(["foo"], IDBTransaction.READ_WRITE); > trans2.objectStore("foo").put("value 2", "mykey"); > trans1.objectStore("foo").put("value 1", "mykey"); > > The put on trans1 will be done first before the put on trans2. The reason is > that trans2 will not be able to grab a lock on object store "foo" until all > pending requests for trans1 are executed. > > This is the expectation our internal partners have been following. In general, > we expect devs to use transactions as they create them. We would like this to > be the spec'ed behavior. > > Israel > If we agree on this, should we add the following text to section 3.1.7 to capture this restriction: "A transaction must not start until all other READ_WRITE transactions with overlapping scope have completed. When multiple transactions are eligible to be started, older transactions should be started first." Israel
Received on Friday, 14 October 2011 20:51:59 UTC