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

On Thu, Jul 1, 2010 at 11:17 AM, Jonas Sicking <jonas@sicking.cc> wrote:

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

This would limit us in the future in terms of schema changes.  The current
async interface differs starting the transaction until the first call that
accesses/modifies data (which are all async).  If we ever allow a schema
change to happen without disconnecting all clients, it'd be possible that
the objectStore could be deleted between when the call is made and when the
transaction is actually allowed to start.

This also will limit what can be done on a background thread.  For example,
an implementation couldn't do serialization of the object on a background
thread (yes, if you did this, you'd need to make sure the main thread didn't
modify it until it finished serializing).

Because of these reasons, I'm not too excited about this particular
heuristic for when to throw vs fire an error callback.  I've thought about
it a bit and can't think of anything better though, unfortunately.

I think I'm still slightly in favor of routing all errors through onerror
callbacks and never throwing from a function that returns an IDBResult, but
I think there were some good points brought up by Jonas for why throwing on
some errors would make sense.


> Let me know what you think.
>
> / Jonas
>
>

Received on Thursday, 1 July 2010 01:43:22 UTC