[IndexedDB] Callback order

Hi All,

One thing that we've talked a bit about at mozilla is about in which
order asynchronous events should fire. I.e. for code like:

results = [];
trans = db.transaction(["mystore"]);
trans.objectStore("mystore").get(1).onsuccess = function(e) {
  results.push(e.result);
}
trans.objectStore("mystore").get(17).onsuccess = function(e) {
  results.push(e.result);
}

Which order should the two onsuccess event callbacks fire in?

We could say that the order is undefined. This gives the
implementation the freedom to fire any even has soon as it possibly
can and is seemingly the best choice for performance. However this has
the significant downside that we don't have cross-browser defined
behavior.

In fact, it's likely that in many implementations the above callback
order would always be that the callback for the get(1) will fire
before the callback to get(17). This would be the case if the
indexedDB store was managed on a single, but separate, thread or
process which does all the reading/writing in the backend database.

This makes it very likely that authors will come to depend on this
being the order, and if any implementation tries to somehow optimize
and create out-of-order firing, then that will lead to sites breaking.

So here we could define the callback order to be that of the order in
which requests are scheduled without loss of performance.

However a tricker situation is cursors. Consider for example code such as:

results = [];
trans = db.transaction(["candysales", "kids"]);
trans.objectStore("candysales").openCursor(range).onsuccess = function(e) {
  cursor = e.result;
  if (cursor) {
     var obj = { candyType: cursor.value.candyType }
     results.push(obj);
     trans.objectStore("kids").get(cursor.value.kidId).onsuccess = function(e) {
       obj.kidName = e.result;
     }
     cursor.continue();
  }
}

or

trans = db.transaction(["opensales", "closedsales"], READ_WRITE);
trans.objectStore("opensales").openCursor(range).onsuccess = function(e) {
  cursor = e.result;
  if (cursor) {
    trans.objectStore("closedsales").add(cursor.value);
    cursor.remove();
    cursor.continue();
  }
}

In these cases for each cursor move, another request is made. If we
were to fire event callbacks in the order the requests are made, it
would mean that we can't fire the second cursor-success until the get
or put has succeeded.

But it is very reasonable to expect that cursors can be implemented by
reading a large number of entries from the database at once and send
them all to the thread where the consumer is running. It would then
not need to round-trip to the indexedDB thread on each call to
cursor.continue();

In such an implementation there would be a very large overhead to have
to wait for the get/add callback to finish before firing the success
callback for each cursor callback.

We could simply say that all cursor callbacks can fire in any order.
However, as usual, this is likely to lead to cross browser differences
and code only working in one browser.

Alternatively we can say that having to wait for other callbacks is
ok. This still allows code that only iterates over an objectStore
using a cursor, without performing any other requests in the meantime,
can still execute at full speed. Only more advanced examples like the
two above take a performance hit.

Or we can define that all cursor callbacks are always fired before any
other scheduled callbacks. So in the two examples above, as soon as
the cursor.continue() call occurs, all other pending callbacks are
prevented until the cursor callback has been fired. Which in an
implementation which reads many cursor values at the same time, is
possibly immediately.

So in the above two examples all the cursor callbacks would fire
before any of the .get/.add callbacks are fired.

Note that we can make the definition be that the cursor has to iterate
all the way through before any other callbacks fire since the webpage
might not always call cursor.continue(). See for example the last
example in http://docs.google.com/document/pub?id=1I__XnwvvSwyjvxi-FAAE0ecnUDhk5DF7L2GI6O31o18

This certainly add a bit of complexity to the implementation. However
it allows us to keep high performance and a defined deterministic
firing order.

Interested to hear your feedback.

/ Jonas

Received on Friday, 18 June 2010 23:09:16 UTC