Re: [WebSimpleDB] Flatting APIs to simplify primary cases

Hi Pablo,

Application feedback is very helpful for the async API. This is one  
area that I have been the least convinced about (poor programmability  
means dissatisfied users).

I am currently investigating the impacts of doing what is needed to  
simplify the async API. At the very least, your proposal will add some  
20 methods to a single interface - some applying to the database,  
others to the store, and the rest to an index.

Additionally, we have to consider the case where more than one request  
is concurrently made to the database.

I will get back to the WG with my findings. In the meanwhile, if you  
have more findings to report, I would be glad to take them into  
consideration.

Thanks,
Nikunj

On Nov 19, 2009, at 12:25 PM, Pablo Castro wrote:

> We're busy creating experimental implementations of WebSimpleDB to  
> both understand what it takes to implement and also to see what the  
> developer experience looks like.
>
> As we started to write "application code" against the API  
> (particularly the async one) the first thing that popped is the fact  
> that you need two levels of nested callbacks for everything. While  
> the current factoring of the API makes sense on the design board,  
> it's kind of noisy in app code. For example:
>
> // assume you already have a database opened in dbReq
> var html = "<ul>";
> var storeReq = new ObjectStoreRequest(dbReq.database);
> storeReq.success = function() {
>    var cursorReq = new CursorRequest(storeReq.store);
>    cursorReq.callback = function(key, cursor, value) {
>        html += "<li>" + value.Name + "</li>";
>    }
>    cursorReq.onsuccess = function(r) {
>        document.getElementById("output").innerHTML = html + "</ul>";
>    }
>    cursorReq.open();
> }
> storeReq.open();
>
> One option that we would like to explore is to "flatten" the API, so  
> most common methods are straight in the database class. This trades  
> off some of the factoring in favor of usability for common cases  
> using the async API.
>
> The change would span a couple of aspects:
>
> 1. Move operations from object store interface and the index  
> interface into the Database interface.
>
> Accessing indexes and stores through specialized objects is  
> problematic for the following reasons:
> - It's always the case that we need to consider when objects are  
> invalidated because something changes from underneath them, for  
> example a schema change. So for example, if there is an explicit  
> store object, then when the store is dropped we need to consider  
> what is valid/invalid and what its failure points and modes are. By  
> not having a standalone store object, we significantly reduce the  
> "gotchas" to consider.
> - From a usability perspective, it's simpler to work with a store in  
> a single step, rather than having to open it first and then work  
> with it (see patterns below with a single request and one DBRequest  
> object).
> - With no "two-step" access pattern, the API has one less level of  
> asynchronicity, as effectively the table lookup + operation are  
> atomic within the store. This also consolidates all operations with  
> an async variant in a single interface (the Database), which is a  
> great simplification for discoverability.
>
> var html = "<ul>";
> var request = asyncDb.forEachStoreObject("contacts", function(row) {
>   html += "<li>" + row.Name + "</li>";
> });
> request.onsuccess = function(r) {
>  document.getElementById("output").innerHTML = html + "</ul>"; }
>
> In moving the operations, it's probably best to rename them to  
> something more descriptive, so we can have for example  
> 'getFromStore(storeName, key)' and 'getFromIndex(storeName,  
> indexName, key)'. This also helps in that 'delete' won't collide  
> with the Javascript keyword.
>
> Note that the store and index interfaces are still around to provide  
> metadata, but at this point they behave as simple read-only snapshots.
>
> 2. Generalize the use of DBRequest, add a 'result' member to it and  
> have all asynchronous operations be initiated from a DatabaseAsync  
> interface.
>
> As a result of the previous changes, all operations that have an  
> async counterpart should now exist on the DatabaseAsync interface.  
> Rather than having multiple types of requests depending on the  
> target object, it is possible to have operations on a DatabaseAsync  
> interface that provide a uniform invocation and handling programming  
> pattern.
>
> This gives a nice pattern for understanding how a sync API maps to  
> an async API.
>
> So for example:
>
> var record = db.getFromStore("store", key); // use record...
>
> Becomes:
>
> var request = asyncDb.getFromStore("store", key); request.onsuccess  
> = function(req) {
>  var record = req.result;
>  // use record...
> };
>
> We could include more data in DBRequest or DBRequest.result as  
> needed if in some cases a method produces more than just a simple  
> result. Further specializatons of DBRequest (subtypes) are still  
> possible in the future if we need to introduce special cases for  
> specific operations.
>
> Similarly, we would have something like asyncDb.forEachStoreObject()  
> that queues a task to call a callback for each element in a store/ 
> index, potentially within a range if specified. The pattern scales  
> well to all the other APIs present in db/store/index today.
>
> If this seems like a good idea to folks, we'd be happy to write up a  
> more complete version that articulates the tweaks across all the  
> WebSimpleDB APIs to make this happen.
>
> Regards,
> -pablo
>
>

Nikunj
http://o-micron.blogspot.com

Received on Friday, 11 December 2009 19:04:07 UTC