- From: Jonas Sicking <jonas@sicking.cc>
- Date: Fri, 6 Aug 2010 08:04:20 -0700
- To: Jeremy Orlow <jorlow@chromium.org>
- Cc: Shawn Wilsher <sdwilsh@mozilla.com>, public-webapps WG <public-webapps@w3.org>
On Fri, Aug 6, 2010 at 6:52 AM, Jeremy Orlow <jorlow@chromium.org> wrote: > On Fri, Aug 6, 2010 at 1:56 PM, Jeremy Orlow <jorlow@chromium.org> wrote: >> >> On Thu, Aug 5, 2010 at 8:56 PM, Jonas Sicking <jonas@sicking.cc> wrote: >>> >>> Ok, I'm going to start by taking a step back here. >>> >>> There is no such thing as implicit transactions. >>> >>> db.objectStore("foo", mode) >>> >>> is just syntactic sugar for >>> >>> db.transaction(["foo"], mode).objectStore("foo") >>> >>> so it always starts a new transaction. I think for now, lets take >>> db.objectStore(..) out of the discussion and focus on how we want >>> db.transaction() to work. In the end we may or may not want to keep >>> db.objectStore() if it causes too much confusion. >>> >>> One thing that we have to first realize is that every IDBObjectStore >>> instance is tied to a specific transaction. This is required to avoid >>> ambiguity in what transaction a request is made against. Consider the >>> following code >>> >>> trans1 = db.transaction(["foo", "bar"], READ_WRITE); >>> trans2 = db.transaction(["foo", "students"], READ_ONLY); >>> os1 = trans1.objectStore("foo"); >>> os2 = trans2.objectStore("foo"); >>> alert(os1 === os2); >>> os1.get("someKey").onsuccess = ...; >>> >>> In this code, the alert will always display "false". The os1 and os2 >>> are two distinct objects. They have to be, otherwise we wouldn't know >>> which transaction to place the get() request against. >>> >>> Once a transaction has been committed or aborted, using any of the >>> IDBObjectStore objects connected with it will throw an error. So the >>> example mentioned earlier in the thread (i'll use different syntax >>> than used previously in the thread): >>> >>> var gMyos = null; >>> function fun1() { >>> gMyos = db.transaction(["foo"]).objectStore("foo"); >>> gMyos.get("someKey").onsuccess = ...; >>> } >>> function fun2() { >>> gMyos.get("someOtherKey"); >>> } >>> >>> If we return to the main even loop between calling fun1 and fun2, the >>> .get() call in fun2 will *always* throw. IMHO it's a good thing that >>> this consistently throws. Consider also >>> >>> function fun3() { >>> var trans = db.transaction(["foo", "bar"], READ_WRITE); >>> trans.objectStore("bar").openCursor(...).onsuccess = ...; >>> } >>> >>> It would IMHO be a bad thing if calling fun3 right before calling fun2 >>> all of a sudden made fun2 not throw and instead place a request >>> against the transaction created in fun3. >>> >>> While I definitely think it can be confusing that there are several >>> IDBObjectStore instances referring to the same underlying objectStore, >>> I think this is ultimately a good thing as it reduces the risk of >>> accidentally placing a request against the wrong transaction. It means >>> that in order to place a request against a transaction, you must >>> either have a reference to that transaction, or a reference to an >>> objectStore retrieved from that transaction. >>> >>> Another way to think of it is this. You generally don't place requests >>> against an objectStore or index. You place them against a transaction. >>> By tying IDBObjectStores to a given transaction, it's always explicit >>> which transaction you are using. >>> >>> On Thu, Aug 5, 2010 at 3:04 AM, Jeremy Orlow <jorlow@chromium.org> wrote: >>> > On Wed, Aug 4, 2010 at 7:47 PM, Shawn Wilsher <sdwilsh@mozilla.com> >>> > wrote: >>> >> >>> >> On 8/4/2010 10:53 AM, Jeremy Orlow wrote: >>> >>> >>> >>> >>> >>> Whoa....transaction() is synchronous?!? Ok, so I guess the entire >>> >>> premise >>> >>> of my question was super confused. :-) >>> >>> >>> >> It is certainly spec'd that way [1]. The locks do not get acquired >>> >> until >>> >> the first actual bit of work is done though. >>> > >>> > I fully understand how the trick works. I just didn't comprehend the >>> > fact >>> > that the Mozilla proposal (what's now in the spec) was removing any way >>> > to >>> > get into an IDBTransactionEvent handler besides doing an initial data >>> > access. I wouldn't have agreed to the proposal had I realized this. >>> > Lets say I had the following bit of initialization code in my program: >>> > var myDB = ... >>> > var myObjectStore = myDB.objectStore("someObjectStore"); >>> > var myIndex = myObjectStore.index("someIndex"); >>> > var anotherObjectStore = myDB.objectStore("anotherObjectStore"); >>> >>> As described above, grabbing references like this is not what you want >>> to do. If we were to allow this I think we would run a severe risk of >>> making it very hard to understand which transaction you are placing >>> requests against. >>> >>> > And then I wanted to start a transaction that'd access some key and >>> > then >>> > presumably do some other work. As currently specced, here's what I'd >>> > need >>> > to do: >>> > >>> > myDB.transaction().objectStore("someObjectStore").index("someIndex").get("someKey").onsuccess(function() >>> > { >>> > anotherObjectStore.get("someOtherKey").onsuccess(...); >>> > }); >>> > vs doing something like this: >>> > myDB.asyncTransaction().onsuccess(function() { >>> > myIndex.get("someKey").onsuccess(function() { >>> > anotherObjectStore.get("someOtherKey").onsuccess(...); >>> > }); >>> > }); >>> > With the former, we actually have more typing and the code is harder to >>> > read. Sure, when I'm writing short code snipits, the synchronous form >>> > can >>> > be more convenient and readable, but forcing this upon every situation >>> > is >>> > going to be a hinderance. >>> > Please, lets add back in a transaction method that returns an >>> > IDBRequest. >>> >>> The current design of the spec is that which IDBObjectStore you place >>> a request against determines which transaction you are using. >>> >>> It sounds to me like you want a design where each IDBObjectStore >>> instance represents just a objectStore name, and depending on *when* >>> you place a request against it determines which transaction is used. >>> >>> To me the latter is a much more subtle and risky. Especially if we >>> *also* want to have the ability to synchronously start transactions. >>> Which transaction would the following code snippets use for the .get() >>> request? >>> >>> Example 1: >>> trans1 = db.transaction(["foo", "bar"], READ_WRITE); >>> trans2 = db.transaction(["foo", "fish"]); >>> myStashedFooObjectStoreReference.get("someKey").onsuccess = ...; >>> >>> Example 2: >>> trans = db.transaction(["foo"], READ_WRITE); >>> var fooStore = trans.objectStore("foo"); >>> fooStore.add("someKey", someValue).onsuccess = function(e) { >>> trans2 = db.transaction(["foo", "bar"]); >>> fooStore.get("someOtherKey"); >>> fooStore = trans2.objectStore("foo"); >>> fooStore.get("aThirdKey"); >>> } >>> >>> Basically if we want to make *when* you place a request determine >>> which transaction is used, rather than *which* IDBObjectStore you use, >>> then we can't allow transactions to be synchronously created. That >>> reason alone is enough to make me think that we should stick with the >>> current model in the spec. >>> >>> Here is what your example above would look like: >>> trans = myDB.transaction(...); >>> >>> trans.objectStore("someObjectStore").index("someIndex").get("someKey").onsuccess >>> = ...; >>> trans.objectStore("anotherObjectStore").get("someOtherKey").onsuccess = >>> ...; >>> >>> Which I think is simpler than either of your examples. Yes, it's a bit >>> more typing, but it's much less asynchronous nesting which is IMHO a >>> bigger advantage. >> >> Ok, I think I now understand how your transaction model was intended to >> work. It scares me some that neither I nor Andrei understood the model. >> I'm curious to know whether it was just us or Pablo and Nikunj were also >> confused by it. >> Personally, I think the model of ObjectStores, Indexes, etc not being tied >> to just one transaction and instead using knowledge of which >> IDBTransactionEvent is currently firing to know which transaction an action >> is tied to is more elegant. But I agree such a model is kind of at odds >> with your model and that just doing one or the other is probably for the >> best. I worry that your model will only increase the pressure for people to >> wrap IndexedDB for syntactic sugar. Yet I also think more thrashing of the >> spec is a bad idea and clearly you guys feel pretty strongly about this. So >> I guess we should continue down our current path. >> I'll file a bug for clarification of what's currently there. > > Hmm. One more question. Does the following fail or work? > trans = myDB.transaction(...); > setTimeout(function() { > trans.objectStore("someObjectStore").index("someIndex").get("someKey").onsuccess = > ...; > }, 0); > In other words, will the transaction commit because we leave JavaScript and > nothing more is queued up in the transaction or will such semantics only > happen after we see the first request be queued up within a transaction? It will not work. The transaction is automatically committed as soon as control returns to the event loop if there are no requests placed against it. I guess technically we could make it work. However I think it would be more inconsistent and confusing to make it so. / Jonas
Received on Friday, 6 August 2010 15:05:13 UTC