[IndexedDB] Should .add/.put/.update throw when called in read-only transaction?

Hi All,

Currently the IndexedDB specification is silent on what should happen
if IDBObjectStore.add, IDBObjectStore.put, IDBObjectStore.remove,
IDBCursor.update or IDBCursor.remove() is called from a READ_ONLY
transaction. There are two possible ways we can handle this:

1. We can throw an exception.
2. We can return a IDBRequest object and asynchronously fire a 'error'
event on this object.

The advantage of 1 is that we pretty much know that this was an error
due to a bug in the web page, and we can always know this
synchronously without having to consult the database. Throwing an
error means that all the existing infrastructure for error handling
with automatically kick in. For example any higher-level try/catch
constructs will have an opportunity to catch the error.
Implementations generally report uncaught exceptions to an error log.
The browser will fire an 'error' event on the window which the page
can use for further logging. Firing an error event on the other hand
does not allow the browser to automatically log the error in a console
as the page hasn't yet gotten a chance to handle it.

The advantage of 2 is that this is consistent with other error
conditions, such as writing duplicate keys, disk errors during writing
the database to disk, internal errors in the database, etc.

While consistency, and only needing to check for errors one way, is
certainly good arguments, I would argue that people won't need to
check for calling-add-on-read-only-transactions. For properly written
code it's not an error that will occur, and thus there is no need to
check for it. In fact, you probably are generally better off letting
the exception bubble all the way up and get logged or caught by
generic error handlers.

Additionally, the structured clone algorithm, which defines that an
exception should synchronously be thrown if the object is malformed,
for example if it consists of a cyclic graph. So .add/.put/.update can
already throw under certain circumstances.

Also compare to if we were using a different API strategy of making
objectStores and cursors returned from READ_ONLY transactions not have
mutating functions. In this case if someone tried to call .put(), that
also would result in a exception from the JS interpreter stating that
you're calling a function that doesn't exist.

So I would argue that we should throw for at least all transaction
violations. I.e. whenever you try to perform an action not allowed by
the current transaction. This would also cover the case of calling
createObjectStore/removeObjectStore/createIndex/removeIndex during a
non-setVersion-transaction.


There is also another case where synchronously know that an error will
be reported. We could throw when IDBCursor.update() is called when the
underlying object store uses in-line keys and the property at the key
path does not match the key in this cursor's position. In this case we
similarly immediately know that there is an error without having to
consult the database. We also generally can be sure that there is a
bug in the web page which would benefit from being reported like other
bugs are.

And like stated above, IDBCursor.update() can already throw if the
passed in object can't be structurally cloned.


Jeremy previously asked if there was a test we could use to
clearly/intuitively break error conditions into two groups. Ones that
cause exceptions to be thrown, and ones that cause error events to be
fired. I would say that errors that do not depend on what data is in
the database, but rather are clearly due to errors at the call site
should throw an exception.

Let me know what you think.

/ Jonas

Received on Thursday, 1 July 2010 01:18:23 UTC