Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Non-transactional nature of AsyncStorage

427 views
Skip to first unread message

Vicamo Yang

unread,
Jul 9, 2014, 7:24:34 AM7/9/14
to mozilla-...@lists.mozilla.org
Hi Gaia developers,

Recently we have still problems in apps utilizing AsyncStorage,
especially in CostControl. I think it's time to deliver a serious warning.

In bug 936703 [1], we moved callbacks into IndexedDB transaction
oncomplete event so closing the app itself in a callback no longer
triggers an AbortError. In bug 994819 [2], we stitched callbacks up so
that we don't have companion transactions in action when we're going to
close the app. We also found similar problem in FTU app in bug 998255
[3]. However, all these works are not sufficient enough to prevent
further AbortError from popping up just as expected [4]. Bug 1033095 [5]
reports yet another possible AbortError case and it's still unreplicable.

The root problem is AsyncStorage API discards the transactional nature
that the original IndexedDB API offers.

All method calls provided by AsyncStorage own their own IndexedDB
transactions. After bug 936703 we have the guarantee that a callback is
only invoked after the bounded transaction has completed. But when it
comes to cascaded transactions like:

asyncStorage.getItem(..., function() {
asyncStorage.setItem(...);
});

One must be fully aware that the two AsyncStorage calls do not therefore
get combined as an atomic operation. That is, any number of additional
AsyncStorage calls can take place between the two lines. For example, if
your app contains entry points for some arbitrary user events, the
|getItem()| line may be reached several times before the callback
function of the first |getItem()| call will ever be invoked. By the
time you're going to manipulate the data that you just retrieved from
the last |getItem()| call and write it back, it has been overwritten by
a previous |setItem()| calls, and your app ends up with a wrong record
written. This can be better illustrated as:

getItem1 => getItem2 => getItem3 => ...
| | |
V V V
setItem1 => setItem2 => setItem3 => ...

Because |getItem()| calls are read only, they might be dispatched right
away and all |setItem()| calls are blocked after all |getItem()| calls
were finished. So basically all |getItem()| calls return the same
result, and now multiple |setItem()| calls are going to write their own
revisions.

With a simple `grep` in Gaia source folder, it seems almost all apps
more or less use AsyncStorage. You can even easily find some of them
have exactly the same get-and-set pattern from grep's output. Most of
the time it just works. It works for the reason that there just don't
have such call flow, or there are other constraints that such scenario
doesn't happen in real world luckily. It's certainly not the best we can
do to hide this problem under pillows. You dream about it. So, with a
short talk with :timdream, we wonder what can we do for this issue now:

1) Rewrite AsyncStorage and let it provides transaction based APIs,
2) Fix CostControl app only, and call IndexedDB APIs directly,
3) Build a transaction queue into AsyncStorage and execute queue
elements one by one,
4) ...

Your comments are welcome.

[1]: https://bugzilla.mozilla.org/show_bug.cgi?id=936703
[2]: https://bugzilla.mozilla.org/show_bug.cgi?id=994819
[3]: https://bugzilla.mozilla.org/show_bug.cgi?id=998255
[4]: https://bugzilla.mozilla.org/show_bug.cgi?id=994819#c30
[5]: https://bugzilla.mozilla.org/show_bug.cgi?id=1033095

Regards,
Vicamo Yang

Julien Wajsberg

unread,
Jul 9, 2014, 8:40:13 AM7/9/14
to dev-...@lists.mozilla.org, Vicamo Yang
We need a simple API to do localStorage-like things. So I'd advise to
introduce a transaction-based API to asyncStorage that would be backward
compatible (read: use the same indexeddb data) with what we have now.

The transaction-based API would be used for cases like
getItem-then-setItem, but other simple cases wouldn't need it.

I could imagine an API like this:

asyncStorage.transaction().then(function(worker) {
return worker.getItem('xxx').then(function(value) {
return worker.setItem('yyy', value+1);
});
});

"transaction()" would not return a simple native Promise object. Rather,
its "then" method would transparently add a `.then(worker.commit,
worker.abort)` at the end of the chain. This way it's easy to complete
or abort the transaction, simply returning a resolved or rejected promise.

If it does not sound good to change "then" semantics, maybe it's better
to rename it to something, like "task" or "do" or "work".

Le 09/07/2014 13:24, Vicamo Yang a �crit :
> _______________________________________________
> dev-gaia mailing list
> dev-...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-gaia

Vicamo Yang

unread,
Jul 9, 2014, 9:41:48 AM7/9/14
to Julien Wajsberg, dev-...@lists.mozilla.org
On 7/9/14, 8:40 PM, Julien Wajsberg wrote:
> We need a simple API to do localStorage-like things. So I'd advise to
> introduce a transaction-based API to asyncStorage that would be backward
> compatible (read: use the same indexeddb data) with what we have now.
>
> The transaction-based API would be used for cases like
> getItem-then-setItem, but other simple cases wouldn't need it.
>
> I could imagine an API like this:
>
> asyncStorage.transaction().then(function(worker) {
> return worker.getItem('xxx').then(function(value) {
> return worker.setItem('yyy', value+1);
> });
> });

I was thinking almost the same thing, except we might need a
read-only/read-write flag for that transaction() call because we'll
never know if we should instantiate a read-write transaction instead of
a read-only one.

The biggest problem for inventing such transactional API is that it
implies these callbacks are called in IDBRequest onsuccess event
handler, not transaction oncomplete handler. Effectively revert of bug
936703. But I think this can be resolved by moving such lines into an
additional oncomplete callback.

Another problem is the success callback of a write operation is no
longer a strong guarantee that it has been done. Any further error that
aborts the transaction will result in all previous write operations
being rolled back. Therefore setItem callbacks should only manipulate
the records to be written, and one have to move modifications to foreign
variables into oncomplete.

> "transaction()" would not return a simple native Promise object. Rather,
> its "then" method would transparently add a `.then(worker.commit,
> worker.abort)` at the end of the chain. This way it's easy to complete
> or abort the transaction, simply returning a resolved or rejected promise.

But we will never know when is the end of the chain. For example:

asyncStorage.transaction().then(function(worker) {
yet_another_async_api(function() {
return worker.getItem('xxx');
});
});

Original AsyncStorage API "works" in this case.

Vicamo Yang

unread,
Jul 9, 2014, 9:41:48 AM7/9/14
to Julien Wajsberg, dev-...@lists.mozilla.org
On 7/9/14, 8:40 PM, Julien Wajsberg wrote:
> We need a simple API to do localStorage-like things. So I'd advise to
> introduce a transaction-based API to asyncStorage that would be backward
> compatible (read: use the same indexeddb data) with what we have now.
>
> The transaction-based API would be used for cases like
> getItem-then-setItem, but other simple cases wouldn't need it.
>
> I could imagine an API like this:
>
> asyncStorage.transaction().then(function(worker) {
> return worker.getItem('xxx').then(function(value) {
> return worker.setItem('yyy', value+1);
> });
> });

I was thinking almost the same thing, except we might need a
read-only/read-write flag for that transaction() call because we'll
never know if we should instantiate a read-write transaction instead of
a read-only one.

The biggest problem for inventing such transactional API is that it
implies these callbacks are called in IDBRequest onsuccess event
handler, not transaction oncomplete handler. Effectively revert of bug
936703. But I think this can be resolved by moving such lines into an
additional oncomplete callback.

Another problem is the success callback of a write operation is no
longer a strong guarantee that it has been done. Any further error that
aborts the transaction will result in all previous write operations
being rolled back. Therefore setItem callbacks should only manipulate
the records to be written, and one have to move modifications to foreign
variables into oncomplete.

> "transaction()" would not return a simple native Promise object. Rather,
> its "then" method would transparently add a `.then(worker.commit,
> worker.abort)` at the end of the chain. This way it's easy to complete
> or abort the transaction, simply returning a resolved or rejected promise.

But we will never know when is the end of the chain. For example:

asyncStorage.transaction().then(function(worker) {
yet_another_async_api(function() {
return worker.getItem('xxx');
});
});

Original AsyncStorage API "works" in this case.

Vicamo Yang

unread,
Jul 9, 2014, 9:41:48 AM7/9/14
to Julien Wajsberg, dev-...@lists.mozilla.org
On 7/9/14, 8:40 PM, Julien Wajsberg wrote:
> We need a simple API to do localStorage-like things. So I'd advise to
> introduce a transaction-based API to asyncStorage that would be backward
> compatible (read: use the same indexeddb data) with what we have now.
>
> The transaction-based API would be used for cases like
> getItem-then-setItem, but other simple cases wouldn't need it.
>
> I could imagine an API like this:
>
> asyncStorage.transaction().then(function(worker) {
> return worker.getItem('xxx').then(function(value) {
> return worker.setItem('yyy', value+1);
> });
> });

I was thinking almost the same thing, except we might need a
read-only/read-write flag for that transaction() call because we'll
never know if we should instantiate a read-write transaction instead of
a read-only one.

The biggest problem for inventing such transactional API is that it
implies these callbacks are called in IDBRequest onsuccess event
handler, not transaction oncomplete handler. Effectively revert of bug
936703. But I think this can be resolved by moving such lines into an
additional oncomplete callback.

Another problem is the success callback of a write operation is no
longer a strong guarantee that it has been done. Any further error that
aborts the transaction will result in all previous write operations
being rolled back. Therefore setItem callbacks should only manipulate
the records to be written, and one have to move modifications to foreign
variables into oncomplete.

> "transaction()" would not return a simple native Promise object. Rather,
> its "then" method would transparently add a `.then(worker.commit,
> worker.abort)` at the end of the chain. This way it's easy to complete
> or abort the transaction, simply returning a resolved or rejected promise.

But we will never know when is the end of the chain. For example:

asyncStorage.transaction().then(function(worker) {
yet_another_async_api(function() {
return worker.getItem('xxx');
});
});

Original AsyncStorage API "works" in this case.

Julien Wajsberg

unread,
Jul 9, 2014, 10:22:03 AM7/9/14
to Vicamo Yang, dev-...@lists.mozilla.org
Le 09/07/2014 15:41, Vicamo Yang a �crit :
> On 7/9/14, 8:40 PM, Julien Wajsberg wrote:
>> We need a simple API to do localStorage-like things. So I'd advise to
>> introduce a transaction-based API to asyncStorage that would be backward
>> compatible (read: use the same indexeddb data) with what we have now.
>>
>> The transaction-based API would be used for cases like
>> getItem-then-setItem, but other simple cases wouldn't need it.
>>
>> I could imagine an API like this:
>>
>> asyncStorage.transaction().then(function(worker) {
>> return worker.getItem('xxx').then(function(value) {
>> return worker.setItem('yyy', value+1);
>> });
>> });
> I was thinking almost the same thing, except we might need a
> read-only/read-write flag for that transaction() call because we'll
> never know if we should instantiate a read-write transaction instead of
> a read-only one.
>
> The biggest problem for inventing such transactional API is that it
> implies these callbacks are called in IDBRequest onsuccess event
> handler, not transaction oncomplete handler. Effectively revert of bug
> 936703. But I think this can be resolved by moving such lines into an
> additional oncomplete callback.
>
> Another problem is the success callback of a write operation is no
> longer a strong guarantee that it has been done. Any further error that
> aborts the transaction will result in all previous write operations
> being rolled back. Therefore setItem callbacks should only manipulate
> the records to be written, and one have to move modifications to foreign
> variables into oncomplete.


Right. I think we definitely need to use another name than "then" for
the transaction worker. So how about:

asyncStorage.transaction().work(function(worker) {
// we're inside a transaction, don't assume anything is
// written yet.
return worker.getItem('xxx').then(function(value) {
return worker.setItem('yyy', value+1);
}).then(function() {
return useful_value;
});
}).then(function oncomplete(value) {
// change your program variables here
// "value" is the resolved promise's value
}, function onabort(exception) {
// handle the transaction abort exception
});


This could be implemented like this (possibly hiding private stuff in a
Map):

function transaction() {
return new SimpleTransaction();
}

function SimplerTransaction() {
transaction = startTransaction();
this.completedPromise = new Promise(function(resolve, reject) {
transaction.oncomplete = resolve;
});
this.abortedPromise = new Promise(function(resolve, reject) {
transaction.onabort = resolve;
});
this.worker = new TransactionWorker();
}

SimpleTransaction.prototype = {
work: function(workerFunc) {
Promise.resolve().then(workerFunc.bind(null, this.worker)).then(function resolved(value) {
return this.completedPromise.then(value);
}.bind(this), function rejected(exception) {
// abort transaction
// return abortPromise.then(() => throw exception); // or something
});
}
};

function TransactionWorker(transaction) {
this.transaction = transaction;
}

// getItem and setItem return promises instead of using callbacks
TransactionWorker.prototype = {
getItem: ...,
setItem: ...
}


(haven't actually tried this)

>
>> "transaction()" would not return a simple native Promise object. Rather,
>> its "then" method would transparently add a `.then(worker.commit,
>> worker.abort)` at the end of the chain. This way it's easy to complete
>> or abort the transaction, simply returning a resolved or rejected promise.
> But we will never know when is the end of the chain. For example:
>
> asyncStorage.transaction().then(function(worker) {
> yet_another_async_api(function() {
> return worker.getItem('xxx');
> });
> });
>
> Original AsyncStorage API "works" in this case.

You mean, if yet_another_async_api starts calls transaction() too?

Jonas Sicking

unread,
Jul 9, 2014, 6:53:32 PM7/9/14
to Julien Wajsberg, Vicamo Yang, dev-...@lists.mozilla.org
Generally speaking I think it's a bad idea to have to "opt in" to a
transaction. Something like:

asyncStorage.getItem(..., function() {
asyncStorage.setItem(...);
});

Looks very safe while it's not. Would people have called
.transaction() every time that the above pattern appears if it had
been available?

I think instead we should enforce that all get/set operations happen
within a transaction.

We wouldn't have to completely roll back the fix for bug 994819.
Adding the ability to do .commit().then(function() { closePage() })
might help.

/ Jonas

On Wed, Jul 9, 2014 at 7:22 AM, Julien Wajsberg <jwaj...@mozilla.com> wrote:
> Le 09/07/2014 15:41, Vicamo Yang a écrit :
>
>> On 7/9/14, 8:40 PM, Julien Wajsberg wrote:
>>>
>>> We need a simple API to do localStorage-like things. So I'd advise to
>>> introduce a transaction-based API to asyncStorage that would be backward
>>> compatible (read: use the same indexeddb data) with what we have now.
>>>
>>> The transaction-based API would be used for cases like
>>> getItem-then-setItem, but other simple cases wouldn't need it.
>>>
>>> I could imagine an API like this:
>>>
>>> asyncStorage.transaction().then(function(worker) {
>>> return worker.getItem('xxx').then(function(value) {
>>> return worker.setItem('yyy', value+1);
>>> });
>>> });
>>
>> I was thinking almost the same thing, except we might need a
>> read-only/read-write flag for that transaction() call because we'll
>> never know if we should instantiate a read-write transaction instead of
>> a read-only one.
>>
>> The biggest problem for inventing such transactional API is that it
>> implies these callbacks are called in IDBRequest onsuccess event
>> handler, not transaction oncomplete handler. Effectively revert of bug
>> 936703. But I think this can be resolved by moving such lines into an
>> additional oncomplete callback.
>>
>> Another problem is the success callback of a write operation is no
>> longer a strong guarantee that it has been done. Any further error that
>> aborts the transaction will result in all previous write operations
>> being rolled back. Therefore setItem callbacks should only manipulate
>> the records to be written, and one have to move modifications to foreign
>> variables into oncomplete.
>
>
>
> Right. I think we definitely need to use another name than "then" for the
> transaction worker. So how about:
>
> asyncStorage.transaction().work(function(worker) {
> // we're inside a transaction, don't assume anything is
> // written yet.
>
> return worker.getItem('xxx').then(function(value) {
> return worker.setItem('yyy', value+1);
>>> "transaction()" would not return a simple native Promise object. Rather,
>>> its "then" method would transparently add a `.then(worker.commit,
>>> worker.abort)` at the end of the chain. This way it's easy to complete
>>> or abort the transaction, simply returning a resolved or rejected
>>> promise.
>>
>> But we will never know when is the end of the chain. For example:
>>
>> asyncStorage.transaction().then(function(worker) {
>> yet_another_async_api(function() {
>> return worker.getItem('xxx');
>> });
>> });
>>
>> Original AsyncStorage API "works" in this case.
>
>
> You mean, if yet_another_async_api starts calls transaction() too?
>
>

Tim Chien

unread,
Jul 10, 2014, 1:30:53 AM7/10/14
to Jonas Sicking, Vicamo Yang, Julien Wajsberg, dev-...@lists.mozilla.org
To recap what I have discussed with Vicamo before I advice him to send
this e-mail:

1. We want the same API from asyncStorage to hide the complexity to
handle the transactions. Introducing transactions to asyncStorage
reduce the value of the library to something simply converting DOM
requests-based API to chainable/Promise-based API, which is valuable
but not that valuable IMHO.

2. Issued identified around asyncStorage can be handled by proper
application logic. Application might need to implement a queue itself
to make sure there isn't any concurrent transactions or block the UI
when transactions is taking place. I think the UI blocking is
implemented in many apps and they should be able to use asyncStroage
as-is, happily.

3. For the specific issues like Cost Control issue Vicamo is handling
(thank you!), maybe people will realize wrapping asyncStorage calls in
a queue is too complex and it might as well as do proper IndexedDB
transaction management -- in that case it's perfectly fine the said
app should move away from asyncStorage.

4. During offline discussion yesterday, I actually realized we could
implement the said queue inside asyncStorage. I am not sure if it make
sense though (nor it could solve anything), but that's something we
can think of.

To recap, I am all for exposing transactions in the wrapping library,
but please do it somewhere else because asyncStorage is still useful
for many of the use cases. I am looking forward to see a chainable or
Promise-based API which will be very useful.

djf is OOO so we might want to wait for him to come back before doing anything.


On Thu, Jul 10, 2014 at 6:53 AM, Jonas Sicking <jo...@sicking.cc> wrote:
> Generally speaking I think it's a bad idea to have to "opt in" to a
> transaction. Something like:
>
> asyncStorage.getItem(..., function() {
> asyncStorage.setItem(...);
> });
>
> Looks very safe while it's not. Would people have called
> .transaction() every time that the above pattern appears if it had
> been available?
>
> I think instead we should enforce that all get/set operations happen
> within a transaction.
>
> We wouldn't have to completely roll back the fix for bug 994819.
> Adding the ability to do .commit().then(function() { closePage() })
> might help.
>
> / Jonas
>
> On Wed, Jul 9, 2014 at 7:22 AM, Julien Wajsberg <jwaj...@mozilla.com> wrote:
>> Le 09/07/2014 15:41, Vicamo Yang a écrit :
>>
>>> On 7/9/14, 8:40 PM, Julien Wajsberg wrote:
>>>>
>>>> We need a simple API to do localStorage-like things. So I'd advise to
>>>> introduce a transaction-based API to asyncStorage that would be backward
>>>> compatible (read: use the same indexeddb data) with what we have now.
>>>>
>>>> The transaction-based API would be used for cases like
>>>> getItem-then-setItem, but other simple cases wouldn't need it.
>>>>
>>>> I could imagine an API like this:
>>>>
>>>> asyncStorage.transaction().then(function(worker) {
>>>> return worker.getItem('xxx').then(function(value) {
>>>> return worker.setItem('yyy', value+1);
>>>> });
>>>> });
>>>
>>> I was thinking almost the same thing, except we might need a
>>> read-only/read-write flag for that transaction() call because we'll
>>> never know if we should instantiate a read-write transaction instead of
>>> a read-only one.
>>>
>>> The biggest problem for inventing such transactional API is that it
>>> implies these callbacks are called in IDBRequest onsuccess event
>>> handler, not transaction oncomplete handler. Effectively revert of bug
>>> 936703. But I think this can be resolved by moving such lines into an
>>> additional oncomplete callback.
>>>
>>> Another problem is the success callback of a write operation is no
>>> longer a strong guarantee that it has been done. Any further error that
>>> aborts the transaction will result in all previous write operations
>>> being rolled back. Therefore setItem callbacks should only manipulate
>>> the records to be written, and one have to move modifications to foreign
>>> variables into oncomplete.
>>
>>
>>
>> Right. I think we definitely need to use another name than "then" for the
>> transaction worker. So how about:
>>
>> asyncStorage.transaction().work(function(worker) {
>> // we're inside a transaction, don't assume anything is
>> // written yet.
>>
>> return worker.getItem('xxx').then(function(value) {
>> return worker.setItem('yyy', value+1);
>>>> "transaction()" would not return a simple native Promise object. Rather,
>>>> its "then" method would transparently add a `.then(worker.commit,
>>>> worker.abort)` at the end of the chain. This way it's easy to complete
>>>> or abort the transaction, simply returning a resolved or rejected
>>>> promise.
>>>
>>> But we will never know when is the end of the chain. For example:
>>>
>>> asyncStorage.transaction().then(function(worker) {
>>> yet_another_async_api(function() {
>>> return worker.getItem('xxx');
>>> });
>>> });
>>>
>>> Original AsyncStorage API "works" in this case.
>>
>>
>> You mean, if yet_another_async_api starts calls transaction() too?
>>
>>
>> _______________________________________________
>> dev-gaia mailing list
>> dev-...@lists.mozilla.org
>> https://lists.mozilla.org/listinfo/dev-gaia
> _______________________________________________
> dev-gaia mailing list
> dev-...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-gaia



--
Tim Guan-tin Chien, Engineering Manager and Front-end Lead, Firefox
OS, Mozilla Corp. (Taiwan)

Gabriele Svelto

unread,
Jul 10, 2014, 2:28:16 AM7/10/14
to Tim Chien, Jonas Sicking, Vicamo Yang, Julien Wajsberg, dev-...@lists.mozilla.org
On 10/07/2014 07:30, Tim Chien wrote:
> 3. For the specific issues like Cost Control issue Vicamo is handling
> (thank you!), maybe people will realize wrapping asyncStorage calls in
> a queue is too complex and it might as well as do proper IndexedDB
> transaction management -- in that case it's perfectly fine the said
> app should move away from asyncStorage.

Which brings up the question: what's preventing people from using
IndexedDB directly? Maybe we can provide a set of helpers to facilitate
the use of IndexedDB in common scenarios while leaving asyncStorage alone.

From my experience the single biggest issue IndexedDB has right now is
not being Promise-based which makes writing long chains of operations
quite clunky and forces you to rely on callbacks to stitch together
different chains. If we could wrap most of IndexedDB async functionality
within promises I think we'd get ourselves a nicer system with a lower
barrier to entry than pure IndexedDB while still retaining its flexibility.

Gabriele

signature.asc

SALVADOR DE LA PUENTE GONZALEZ

unread,
Jul 10, 2014, 6:35:27 AM7/10/14
to Tim Chien, Jonas Sicking, Vicamo Yang, Julien Wajsberg, dev-...@lists.mozilla.org
Hello all.

Commenting in-line:

On 10/07/14 07:32, Tim Chien wrote:
> To recap what I have discussed with Vicamo before I advice him to send
> this e-mail:
>
> 1. We want the same API from asyncStorage to hide the complexity to
> handle the transactions. Introducing transactions to asyncStorage
> reduce the value of the library to something simply converting DOM
> requests-based API to chainable/Promise-based API, which is valuable
> but not that valuable IMHO.
Partially true. The get-then-set pattern is common enough to take into
account inside the *AsyncStorage* utility but it is true it introduces
concepts out of the scope for a *localStorage* adapter.
>
> 2. Issued identified around asyncStorage can be handled by proper
> application logic. Application might need to implement a queue itself
> to make sure there isn't any concurrent transactions or block the UI
> when transactions is taking place. I think the UI blocking is
> implemented in many apps and they should be able to use asyncStroage
> as-is, happily.
I think the solution is the queue. It is not very complicated (I
implemented one yesterday [1] for a shim replacing mozSettings) but if
you want to simply cover the get-then-set pattern simply add a method
`update(key, updateFunction)`. It behaves like `read()` but it sets the
key to the value returned by the `updateFunction` parameter.
>
> 3. For the specific issues like Cost Control issue Vicamo is handling
> (thank you!), maybe people will realize wrapping asyncStorage calls in
> a queue is too complex and it might as well as do proper IndexedDB
> transaction management -- in that case it's perfectly fine the said
> app should move away from asyncStorage.
The naive form of the queue (just "enqueue everything") is not
complicated but could lead to slow access due to the queue nature.
Introducing parallelism is possible but it can lead to a complex
implementation based on dependency graphs.
>
> 4. During offline discussion yesterday, I actually realized we could
> implement the said queue inside asyncStorage. I am not sure if it make
> sense though (nor it could solve anything), but that's something we
> can think of.
>
> To recap, I am all for exposing transactions in the wrapping library,
> but please do it somewhere else because asyncStorage is still useful
> for many of the use cases. I am looking forward to see a chainable or
> Promise-based API which will be very useful.
Why to not continue with "thenables" (not real ones) as Julien said but
with a little modification? Instead of make
asyncStorage.get('key').then()... which lead to the problem Vicamo
pointed, you could do:

asyncStorage.get('key')
.thenSet('key', function(value) { return value + 1; })
.thenGet('anotherKey', function _continuation() { ... })
.catch(function (error) { ... });


Now you know when the chain ends.

As a final note. Most of developers simply don't use or try to wrap
IndexedDB cause the API is quite complicated to use.

Hope it helps.

[1] https://github.com/lodr/shimmozsettings/
>
> djf is OOO so we might want to wait for him to come back before doing anything.
>
>
> On Thu, Jul 10, 2014 at 6:53 AM, Jonas Sicking <jo...@sicking.cc> wrote:
>> Generally speaking I think it's a bad idea to have to "opt in" to a
>> transaction. Something like:
>>
>> asyncStorage.getItem(..., function() {
>> asyncStorage.setItem(...);
>> });
>>
>> Looks very safe while it's not. Would people have called
>> .transaction() every time that the above pattern appears if it had
>> been available?
>>
>> I think instead we should enforce that all get/set operations happen
>> within a transaction.
>>
>> We wouldn't have to completely roll back the fix for bug 994819.
>> Adding the ability to do .commit().then(function() { closePage() })
>> might help.
>>
>> / Jonas
>>
>> On Wed, Jul 9, 2014 at 7:22 AM, Julien Wajsberg <jwaj...@mozilla.com> wrote:
>>> Le 09/07/2014 15:41, Vicamo Yang a écrit :
>>>
>>>> On 7/9/14, 8:40 PM, Julien Wajsberg wrote:
>>>>> We need a simple API to do localStorage-like things. So I'd advise to
>>>>> introduce a transaction-based API to asyncStorage that would be backward
>>>>> compatible (read: use the same indexeddb data) with what we have now.
>>>>>
>>>>> The transaction-based API would be used for cases like
>>>>> getItem-then-setItem, but other simple cases wouldn't need it.
>>>>>
>>>>> I could imagine an API like this:
>>>>>
>>>>> asyncStorage.transaction().then(function(worker) {
>>>>> return worker.getItem('xxx').then(function(value) {
>>>>> return worker.setItem('yyy', value+1);
>>>>> });
>>>>> });
>>>> I was thinking almost the same thing, except we might need a
>>>> read-only/read-write flag for that transaction() call because we'll
>>>> never know if we should instantiate a read-write transaction instead of
>>>> a read-only one.
>>>>
>>>> The biggest problem for inventing such transactional API is that it
>>>> implies these callbacks are called in IDBRequest onsuccess event
>>>> handler, not transaction oncomplete handler. Effectively revert of bug
>>>> 936703. But I think this can be resolved by moving such lines into an
>>>> additional oncomplete callback.
>>>>
>>>> Another problem is the success callback of a write operation is no
>>>> longer a strong guarantee that it has been done. Any further error that
>>>> aborts the transaction will result in all previous write operations
>>>> being rolled back. Therefore setItem callbacks should only manipulate
>>>> the records to be written, and one have to move modifications to foreign
>>>> variables into oncomplete.
>>>
>>>
>>> Right. I think we definitely need to use another name than "then" for the
>>> transaction worker. So how about:
>>>
>>> asyncStorage.transaction().work(function(worker) {
>>> // we're inside a transaction, don't assume anything is
>>> // written yet.
>>>
>>> return worker.getItem('xxx').then(function(value) {
>>> return worker.setItem('yyy', value+1);
>>>>> "transaction()" would not return a simple native Promise object. Rather,
>>>>> its "then" method would transparently add a `.then(worker.commit,
>>>>> worker.abort)` at the end of the chain. This way it's easy to complete
>>>>> or abort the transaction, simply returning a resolved or rejected
>>>>> promise.
>>>> But we will never know when is the end of the chain. For example:
>>>>
>>>> asyncStorage.transaction().then(function(worker) {
>>>> yet_another_async_api(function() {
>>>> return worker.getItem('xxx');
>>>> });
>>>> });
>>>>
>>>> Original AsyncStorage API "works" in this case.
>>>
>>> You mean, if yet_another_async_api starts calls transaction() too?
>>>
>>>
>>> _______________________________________________
>>> dev-gaia mailing list
>>> dev-...@lists.mozilla.org
>>> https://lists.mozilla.org/listinfo/dev-gaia
>> _______________________________________________
>> dev-gaia mailing list
>> dev-...@lists.mozilla.org
>> https://lists.mozilla.org/listinfo/dev-gaia
>
>


________________________________

Este mensaje y sus adjuntos se dirigen exclusivamente a su destinatario, puede contener información privilegiada o confidencial y es para uso exclusivo de la persona o entidad de destino. Si no es usted. el destinatario indicado, queda notificado de que la lectura, utilización, divulgación y/o copia sin autorización puede estar prohibida en virtud de la legislación vigente. Si ha recibido este mensaje por error, le rogamos que nos lo comunique inmediatamente por esta misma vía y proceda a su destrucción.

The information contained in this transmission is privileged and confidential information intended only for the use of the individual or entity named above. If the reader of this message is not the intended recipient, you are hereby notified that any dissemination, distribution or copying of this communication is strictly prohibited. If you have received this transmission in error, do not read it. Please immediately reply to the sender that you have received this communication in error and then delete it.

Esta mensagem e seus anexos se dirigem exclusivamente ao seu destinatário, pode conter informação privilegiada ou confidencial e é para uso exclusivo da pessoa ou entidade de destino. Se não é vossa senhoria o destinatário indicado, fica notificado de que a leitura, utilização, divulgação e/ou cópia sem autorização pode estar proibida em virtude da legislação vigente. Se recebeu esta mensagem por erro, rogamos-lhe que nos o comunique imediatamente por esta mesma via e proceda a sua destruição

Jonas Sicking

unread,
Jul 13, 2014, 11:34:53 AM7/13/14
to Tim Guan-tin Chien, Vicamo Yang, Julien Wajsberg, dev-...@lists.mozilla.org

On Jul 9, 2014 10:31 PM, "Tim Chien" <timd...@mozilla.com> wrote:
> 2. Issued identified around asyncStorage can be handled by proper
> application logic. Application might need to implement a queue itself
> to make sure there isn't any concurrent transactions or block the UI
> when transactions is taking place. I think the UI blocking is
> implemented in many apps and they should be able to use asyncStroage
> as-is, happily.

Is this really going to remain true if Haida causes us to open the same app in multiple sheets? Possibly even the same page within an app in multiple separate sheets?

How will those two pages coordinate such that they don't write at the same time?

/ Jonas

SALVADOR DE LA PUENTE GONZALEZ

unread,
Jul 14, 2014, 3:50:07 AM7/14/14
to Jonas Sicking, Tim Guan-tin Chien, Vicamo Yang, Julien Wajsberg, dev-...@lists.mozilla.org, MARINA RODRIGUEZ IGLESIAS
That is exactly the problem with Cost Control. We have two UI (app and widget) that can trigger a balance request. We was using locks at asyncStorage levels but the access is not interlocking between frames so it is unpredictable useless.

/ Jonas



_______________________________________________
dev-gaia mailing list
dev-...@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-gaia
0 new messages