Re: [IndexedDB] Implicit transactions

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.

> 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?

> 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.

> 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.

> 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.

> 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?

Thanks,
Andrei

Received on Wednesday, 4 August 2010 16:26:53 UTC