There's a question at the bottom about how best to make transactions be
free of concurrency problems. Input welcome.
On Fri, 23 May 2008, Aaron Boodman wrote:
I noticed one unfortunate thing about the new Database API. Because the
executeSql() callback holds open the transaction, it is easy to
accidentally do intensive work inside there and hold open the
transaction too long. A common mistake might be to do a big select and
then hold open the transaction while the UI is updated. This could hold
open the tx maybe an extra couple hundred ms. A bigger problem would be
to do synchronous XHR (for example, in workers). This could hold open
the tx for seconds.
The right place to do work like this is in transaction()'s success
callback. But because the resultsets aren't easily accessible there, I
think authors are more likely to do work in executeSql()'s success
callback and use transaction()'s success callback less frequently.
Off hand about the best solution I can think of to this problem is to
have some sort of state on the transaction object that gathers the
results.
This is not very satisfying though. Does anyone have a better idea? Or
think this is not a big enough concern to worry about?
I agree that this might be a problem. I'm not sure how to address it.
On Tue, 27 May 2008, Scott Hess wrote:
I think the only way you can really make it better is to put something
like an executeSql() function directly on Database objects, which could
shift the success callback entirely outside the transaction scope. I
think that would reasonably match server-side SQL use (you either send a
bare SELECT with implicit transaction, or use explicit BEGIN/END to
group things which must be consistent).
I don't want to add this yet, in case it turns out we don't really need
it, but implementation experience will definitely tell us whether this is
needed or not and if it is we will have to add it in v2.
On Mon, 26 May 2008, Aaron Boodman wrote:
Quick thing. I don't think the spec makes it clear whether it is allowed
to do this:
var db1 = window.openDatabase("foo", "", "", "");
var db2 = window.openDatabase("foo", "1", "", "");
I think the answer should be "no". Thoughts?
If so, perhaps edit this sentence:
If the database version provided is not the empty string, and the
database already exists but has a different version, then the method
must raise an INVALID_STATE_ERR exception.
To read like this:
If the database version provided is not the empty string, and the
database already exists but has a different version, or no version, then
the method must raise an INVALID_STATE_ERR exception.
Fixed.
On Mon, 26 May 2008, Chris Prince wrote:
I think the spec is technically correct. What's confusing is that the
same line can mean two different things:
// Start with no databases.
// On the 1st call, this line means "create a database,
// and set the version string to the empty string".
var db1 = window.openDatabase("foo", "", "", "");
// On the 2nd call, the meaning has changed to
// "open the 'foo' database, regardless of the version string".
var db2 = window.openDatabase("foo", "", "", "");
Yeah, that's a bit confusing. Not sure what to do about it.
On Mon, 4 Aug 2008, Aaron Boodman wrote:
It seems like you need a way to abort an in-progress transaction. An
easy way to do this would be to add an abort() method to SQLTransaction.
What's the use case? Can we wait until v2 to add this, or is it critical?
On Mon, 4 Aug 2008, Aaron Boodman wrote:
Currently, the database API has an error code for the situation where
you open a transaction for read, then try to write but the database is
locked.
I think that the spec should at least suggest, but perhaps require,
implementors to serialize access to a single database to prevent this
from happening. Without this, developers must wrap every single write
attempt in error handling and retry if a lock error occurs.
It seems likely that developers will forget to do this and it will be a
significant pain point with the API. Applications will seem to work, but
then mysteriously fail in production when users have multiple copies of
the application open.
Even if the developer adds retry logic, it is easy for a page to get
starved by another page.
Serializing access would prevent all these problems at the cost of read
concurrency, which I think is OK for the first version of this API. A
future version of the API could add the concept of "read transactions"
which would allow concurrency, but would fail immediately when you try
to write with them.
I am loath to do this, because of the performance hit. However, what we
could do is say that if the first statement is read-only, they all have to
be, and if the first statement is a write, then it locks the database?
Though that would be rather weird...
We could have a .writeTransaction() and a .readTransaction(), where the
former always run in isolation.
Any preferences?