Re: [IndexedDB] Implicit transactions

I talked to Andrei in person.  He seemed to think this was discussed and
agreed upon sometime earlier but agreed the spec could be more clear.

On Wed, Aug 4, 2010 at 5:46 PM, Jeremy Orlow <jorlow@chromium.org> wrote:

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

Andrei said the second .get (assuming the code is not literal...since
there's no way an onsuccess could fire unless we returned control to
JavaScript) should not fire.


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

This does seem to be loosely what's intended...more below...


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

If we're going to leave implicit transactions in, I'd like to see the spec
say something to the effect of the following:
"An IDBObjectStore is implicitly associated with a transaction when created
outside of the context of an IDBTransactionEvent.  From creation until
returning execution from JavaScript, any operation done on that object or
any of its children will be associated with that transaction.  Anything done
outside of this time window and not done within an IDBTreansactionEvent will
result in _some error_."  (Not super happy with this specific text, but I
hope you get the idea.)

Jonas/Shawn: Since it seems you've been getting some feedback on your
implementation, do you have any data to suggest that implicit transactions
are being used and considered helpful in the wild?

J

Received on Wednesday, 4 August 2010 17:25:41 UTC