Re: [IndexedDB] Implicit transactions

On Wed, Aug 4, 2010 at 5:26 PM, Andrei Popescu <andreip@google.com> wrote:

> On Wed, Aug 4, 2010 at 4:42 PM, Jeremy Orlow <jorlow@chromium.org> wrote:
> > In the IndexedDB spec, there are two ways to create a transaction.  One
> is
> > explicit (by calling IDBDatabase.transaction()) and one is implicit (for
> > example, by calling IDBDatabase.objectStore.get("someKey")).  I have
> > questions about the latter, but before bringing these up, I think it
> might
> > be best to give a bit of background (as I understand it) to make sure
> we're
> > all on the same page:
> >
> > Belief 1:
> > No matter how the transaction is started, any subsequent calls done
> within
> > an IDBTransactionEvent (which is the event fired for almost every
> > IDBRequest.onsuccess call since almost all of them are for operations
> done
> > within the context of a transaction) will continue running in the same
> > transaction.  So, for example, the following code will atomically
> increment
> > a counter:
> > myDB.transaction().onsuccess(function() {
> >
>  myDB.objectStore("someObjectStore").get("counter").onsuccess(function()
> > {
> >         myDB.objectStore("someObjectStore").put("counter", event.result +
> > 1);
> >     });
> > });
> >
> > Belief 2:
> > Let's say I ran the following code:
> > myDB.transaction().onsuccess(function() { window.myObjectStore =
> > myDB.objectStore("someObjectStore"); /* do some other work */ });
> > And then at any point later in the program (after that first transaction
> had
> > committed) I could do the following:
> > myDB.transaction().onsuccess(function() { window.myObjectStore.get("some
> > value").onsuccess(...); });
> > Even though myObjectStore was originally fetched during some other
> > transaction, it's quite clear that I'm accessing values from that object
> > store in this new transaction's context, and thus that's exactly what
> > happens and this is allowed.
> >
>
>
> I think it's only allowed as long as the object store in question is
> in the scope of this other transaction.
>

Of course.  (I should have explicitly mentioned that though.)


>  > Implicitly created transactions:
> > At a high level, the intent is
> > for IDBDatabase.objectStore.get("someKey").onsuccess(...); to "just
> work",
> > even when not called in an IDBTransactionEvent handler.  But what happens
> if
> > I run the following code (outside of an IDBTransactionEvent handler):
> > for (var i=0; i<5; ++i)
> >     myDB.objectStore("someObjectStore").get("someKey").onsuccess(...);
> > Do we want that to create 5 separate transactions or 5 requests within
> the
> > same transaction?
>
> As currently specced, I think that would indeed start 5 separate
> transactions. But couldn't you save the object store in a variable
> before the loop?
>

To be clear, you're suggesting that the following would result in 1
transaction?

var myOS = myDB.objectStore("someObjectStore");
for (var i=0; i<5; ++i)
    myOS.get("someKey").onsuccess(...);

This would seem to imply that, when used outside of an IDBTransactionEvent
context, each instance of an objectStore object will be linked to its own
transaction?  I'd assume that any children (for example IDBIndex objects)
that come from that IDBObjectStore would also be linked to the same
transaction?

What about the following:

var myOS = myDB.objectStore("someObjectStore");
myOS.get("someKey").onsuccess(...);
/* do other stuff for a while...onsuccess above fired and thus the
implicitly created transaction was committed implicitly */
myOS.get("anotherKey).onsuccess(...);

The implicitly created transaction has completed before the second .get()
call.  Would the second call throw or would it start another implicit
transaction?


> > And what if we run my earlier example (that stored an object store to
> > window.myObjectStore within a transaction we started explicitly) and then
> > run the following code (outside of an IDBTransactionEventHandler):
> > window.myObjectStore.get("someKey").onsuccess(...);
> > myDB.objectStore("someObjectStore").get("someKey").onsuccess(...)
> > Should both be legal?  Will this create one or two transactions?
> >
>
> I think simply calling window.myObjectStore.get() would not create a
> transaction. I think it would just throw?
>
> myDB.objectStore().get() would create a transaction.
>

If the second .get in my last example would fail (i.e. ObjectStores are
somehow bound to a transaction, and once that transaction finishes, it
cannot be used outside of an IDBTransactionEvent context), then I could see
this making sense.  Otherwise could you please explain why this is?


> > Speccing such transactions:
> > After thinking about this, I only see a couple options for how to spec
> > implicitly created transactions:
> > "When an operation that needs to be done in a transaction (i.e. anything
> > that touches data) is done outside of an IDBTransactionEvent handler..."
> > 1) "that operation will be done in its own, newly created transaction."
> > 2) "if there already exists an implicitly created transaction for that
> > objectStore, it'll be done in that transaction.  Otherwise a new one will
> be
> > created."
> > 3) "if there already exists _any_ transaction with access to that
> > objectStore, it'll be done in that transaction.  Otherwise a new one will
> be
> > created."
> > 2 seems like it'd match the users intention in a lot of cases, but its
> > biggest problem is that it's non-deterministic.  If you do one .get() and
> > then set a time out and do another, you don't know whether they'll be in
> the
> > same transaction or not.
>
> That's right, it seems like a problem to me.
>
> >  3 seems to have the same problem except it's even
> > less predictable.  So, but process of elimination, it seems as though 1
> is
> > our only option in terms of how to spec this.  Or am I missing something?
> >
>
> Well, what's wrong with what's specced today:
>
> - you can only call get/put/etc in the context of a transaction. If
> you don't, they'll throw.
> - "in the context of a transaction" means in a transaction callback or
> after you created an implicit transaction and until control returns to
> the main browser event loop.
>

The problem is that the latter part is very vague and I don't think we're on
the same page in terms of what it specifically means.  That's why I'm asking
questions with specific examples.


> > Read-only by default too?
> > Another somewhat related question: should implicitly created transactions
> be
> > read-only (which is the default for explicitly created ones)?  If so,
> that
> > means that we expect the following to fail:
> > myDB.objectStore("someObjectStore").get("counter").onsuccess(function() {
> >     myDB.objectStore("someObjectStore").put("counter", event.result + 1);
> > });
> > Unfortunately, it seems as though a lot of use cases for implicitly
> created
> > transactions would involve more than just reads.  But if we spec that to
> > succeed, then we're lowering concurrency and making
> things inconsistent with
> > the default for IDBDatabase.transaction(), right?
> >
>
> Right. But IDBDatabase::objectStore() takes a mode parameter. You can
> pass READ_WRITE if you want.
>

Hm.  Gotcha.  Still seems like it's making those nice 1-liners enough less
nice that maybe implicit transactions aren't worth it.  :-)

 > Conclusion:
> > Am I missing something here?  Or will properly speccing implicitly
> created
> > transactions lead to them being nearly useless for the simple use cases
> we
> > were trying to make simpler to begin with?
>
> Not sure, perhaps we need better wording in the spec to explain how they
> work?
>

I definitely think we need more clear wording in the spec.  But first I
think we need to make sure we all agree on what the spec is trying to say.

J

Received on Wednesday, 4 August 2010 16:47:48 UTC