- From: Jeremy Orlow <jorlow@chromium.org>
- Date: Wed, 14 Jul 2010 09:02:08 +0100
- To: Jonas Sicking <jonas@sicking.cc>
- Cc: Webapps WG <public-webapps@w3.org>
- Message-ID: <AANLkTilqpo0hmVbByWD5FlMNmd0Z9goIAro7uR8qjjWK@mail.gmail.com>
On Wed, Jul 14, 2010 at 8:53 AM, Jonas Sicking <jonas@sicking.cc> wrote: > On Tue, Jul 13, 2010 at 11:33 PM, Jeremy Orlow <jorlow@chromium.org> > wrote: > > On Wed, Jul 14, 2010 at 7:28 AM, Jonas Sicking <jonas@sicking.cc> wrote: > >> > >> On Tue, Jul 13, 2010 at 11:12 PM, Jeremy Orlow <jorlow@chromium.org> > >> wrote: > >> > On Tue, Jul 13, 2010 at 9:41 PM, Jonas Sicking <jonas@sicking.cc> > wrote: > >> >> > >> >> On Tue, Jul 13, 2010 at 1:17 PM, Jeremy Orlow <jorlow@chromium.org> > >> >> wrote: > >> >> > On Tue, Jul 13, 2010 at 8:25 PM, Jonas Sicking <jonas@sicking.cc> > >> >> > wrote: > >> >> >> > >> >> >> Hi All, > >> >> >> > >> >> >> Sorry if this is something that I've brought up before. I know I > >> >> >> meant > >> >> >> to bring this up in the past, but I couldn't find any actual > emails. > >> >> >> > >> >> >> One thing that we discussed while implementing IndexedDB was what > to > >> >> >> do for IDBRequest.abort() or "writing" requests. For example on > the > >> >> >> request object returned from IDBObjectStore.remove() or > >> >> >> IDBCursor.update(). > >> >> >> > >> >> >> Ideal would of course be if it would cancel the write operation, > >> >> >> however this isn't always possible. If the call to .abort() comes > >> >> >> after the write operation has already executed in the database, > but > >> >> >> before the 'success' event has had a chance to fire. What's worse > is > >> >> >> that other write operations might already have been performed on > top > >> >> >> of the aborted request. Consider for example the following code: > >> >> >> > >> >> >> req1 = myObjectStore.remove(12); > >> >> >> req2 = myObjectStore.add({ id: 12, name: "Benny Andersson" }); > >> >> >> .... do other stuff .... > >> >> >> req1.abort(); > >> >> >> > >> >> >> In this case, even if the database supported aborting a specific > >> >> >> operation, it's very hard to say what the correct thing to do with > >> >> >> operations performed after it. As far as I know, databases > generally > >> >> >> don't support rolling back a given operation, only rolling back to > a > >> >> >> specific point, i.e. rolling back a given operation and all > >> >> >> operations > >> >> >> performed after it. > >> >> >> > >> >> >> We could say that abort() signals some sort of error if the > >> >> >> operation > >> >> >> has already been performed in the database, however that makes > >> >> >> abort() > >> >> >> very racy. > >> >> >> > >> >> >> Instead we concluded that the best thing to do was to specify that > >> >> >> IDBRequest.abort() should throw if called on a modifying request. > If > >> >> >> this sounds good I'll make this change to the spec. > >> >> > > >> >> > I'd be fine with that. > >> >> > Or we could remove abort all together. I can't really think of > what > >> >> > types > >> >> > of operations you'd really want to abort until (at least) we have > >> >> > some > >> >> > sort > >> >> > of join language or other mechanism to do really expensive > read-only > >> >> > calls. > >> >> > >> >> I think there are expensive-ish read-only calls. Indexes are > >> >> effectively a join mechanism since you'll hit one b-tree to do the > >> >> index lookup, and then a second b-tree to look up the full object in > >> >> the objectStore. > >> > > >> > But each individual call (the scope of canceling an IDBRequest) is > >> > pretty > >> > short. > >> > > >> >> > >> >> I don't really feel strongly either way. I think abort() isn't too > >> >> hard to implement, but also doesn't provide a ton of value. At least > >> >> not, like you say, until we add expensive calls like getAll or > >> >> multi-step joins. > >> > > >> > I agree that when we look at adding such calls we may want to add an > >> > abort > >> > on just IDBRequest, but until then I don't think it's a very useful > >> > feature. > >> > And being easy to add is not a good reason to lock ourselves into > >> > a particular design in the future. I think we should remove it until > >> > there's a good reason for it to exist. > >> > > >> >> > >> >> > Or we could take abort off IDBRequest and instead put a rollback on > >> >> > transactions (and not do the modify limitation). > >> >> > >> >> I definitely think we should have IDBTransaction.abort() no matter > >> >> what. And that should allow rolling back write operations. > >> > > >> > Agreed. In which case it seems as though being able to abort > individual > >> > operations isn't that important...especially given what we just talked > >> > about > >> > above. > >> > So can we just get rid of abort() on IDBRequest? > >> > >> I don't feel strongly either way. We'll probably keep them in the > >> mozilla implementation since we have experimental > >> objectStore.getAll(key) and index.getAllObjects(key) implementations, > >> which both probably count as long-running. > > > > Sounds good. I'll file a bug to remove them from the spec then (and put > a > > note that if we do have getAll* we should re-add it). > > Actually, I thought of another situation where we currently could have > long-running read requests. > > One implementation strategy for transactions is to not wait for all > objectStores to become available before starting to execute requests. > Instead you start locking objectStores in some implementation defined, > but consistent, order and as soon as you've locked the objectStore at > which the next pending request is placed, you perform that request. As > long as the order in which the implementation locks the objectStores > is the same for all transactions, there is no risk of deadlocks. > > Consider for example the following code: > > trans = db.transaction(["foo", "bar"]); > trans.objectStore("bar").get(12).onsuccess = function(e) { > trans.objectStore("foo").get(e.result.parentId).onsuccess = ...; > } > > If the implementation decided to lock objectStores in alphabetical > order, it could perform the .get() request as soon as it had locked > the "bar" objectStore. > > Note that all of this is possible without any changes to the spec. It > still fulfills all requirements set out by the spec, such as never > resulting in deadlocks, and performing requests in the order they were > made. It's essentially completely transparent to the page, it just > results in that separate transactions can run more concurrently. > Maybe we should add something to the spec then. Doing it this way seems silly. In such an implementation, a simple objectStore.get() request can take > quite a while to execute as it might depend on another transaction > completely finishing. So in such an implementation, pages would > benefit from aggressively aborting requests they no longer care about. > You can still kill the transaction though. Anyway, it seems like the only use case for this stuff is an app detecting something is taking a while and canceling it. Honestly, I don't see this being too common of a use case. I mean, the app would need to set timeouts and keep track of what's been running for a while and then abort it. While that's not too far fetched, assuming an app will do this in a way that doesn't break the rest of the app or mess up transactions does. All the cases I can think of where you'd want to abort a read, it seems like aborting the whole transaction isn't that much worse. Unless someone can come up with some solid use cases for aborting a single read-only IDBRequest and not a whole transaction, I feel strongly we should remove it. J
Received on Wednesday, 14 July 2010 08:02:58 UTC