[WebSimpleDB] Introduce a pause/resume pattern for coordinated access to multiple stores

2009-12-22 Thread Pablo Castro
Whenever we take a callback that's to be called for each item in a set (e.g. 
with a .forEach(callback) pattern), we need a way to indicate the system 
whether it's ok to move to the next row and invoke the next callback or not. 
Otherwise, in scenarios where the callback itself performs an operation that 
doesn't finish immediately (such as another database async call) the system 
will keep queuing up top-level callbacks, which in turn may queue up more 
callbacks as part of its implementation, and execution will be in some order 
that's very hard to predict at best.

This comes up in several contexts. Applications will often need to scan more 
than one object store in coordination. Query processors will also need this 
when implementing physical operators for joins and such. A different context 
would be a system that needs to submit an HTTP request per row, where you may 
want to use an XmlHttpRequest and unwind after calling open. While the HTTP 
request is in flight you don't want to move to the next

In most cases one of the key aspects is that we need separate components to 
work cooperatively as they pull rows from one or multiple scans, and there 
needs to be a way of controlling the advance of cursors through the rows.

We would like to introduce pause and resume functions for scans to support 
this. Since there is no obvious place to put this right now, we could introduce 
an iterator object that can be used to control things related to the current 
state of the iteration as of when the callback happens, or maybe this is the 
cursor itself.

The resulting code would look like this (the example uses the 
single-async-level pattern we're playing around, but these two are actually 
independent things):

async_db.forEachObjectInStore(people, function(person, iteration) {
  iteration.pause(); // we won't be done with 'person' until later...
  var request = async_db.getFromStore(people, person.managerId);
  request.onsuccess = function() {
var manager = request.result;
// Do something with both 'person' and 'manager', and now we're ready to 
process the next person.
iteration.resume();
  };
});

The nice thing about adding these as methods on the side is that it's 
completely out of sight in simple scenarios where you may be just scanning to 
build some HTML for example. Only if you're doing multiple coordinated, async 
tasks you need to know about these functions.

Regards,
-pablo




Re: [WebSimpleDB] Introduce a pause/resume pattern for coordinated access to multiple stores

2009-12-22 Thread Nikunj R. Mehta


On Dec 22, 2009, at 5:39 PM, Pablo Castro wrote:

Whenever we take a callback that's to be called for each item in a  
set (e.g. with a .forEach(callback) pattern), we need a way to  
indicate the system whether it's ok to move to the next row and  
invoke the next callback or not. Otherwise, in scenarios where the  
callback itself performs an operation that doesn't finish  
immediately (such as another database async call) the system will  
keep queuing up top-level callbacks, which in turn may queue up more  
callbacks as part of its implementation, and execution will be in  
some order that's very hard to predict at best.


This comes up in several contexts. Applications will often need to  
scan more than one object store in coordination. Query processors  
will also need this when implementing physical operators for joins  
and such. A different context would be a system that needs to submit  
an HTTP request per row, where you may want to use an XmlHttpRequest  
and unwind after calling open. While the HTTP request is in flight  
you don't want to move to the next


In most cases one of the key aspects is that we need separate  
components to work cooperatively as they pull rows from one or  
multiple scans, and there needs to be a way of controlling the  
advance of cursors through the rows.


We would like to introduce pause and resume functions for scans  
to support this. Since there is no obvious place to put this right  
now, we could introduce an iterator object that can be used to  
control things related to the current state of the iteration as of  
when the callback happens, or maybe this is the cursor itself.


The resulting code would look like this (the example uses the single- 
async-level pattern we're playing around, but these two are actually  
independent things):


async_db.forEachObjectInStore(people, function(person, iteration) {
 iteration.pause(); // we won't be done with 'person' until later...
 var request = async_db.getFromStore(people, person.managerId);
 request.onsuccess = function() {
   var manager = request.result;
   // Do something with both 'person' and 'manager', and now we're  
ready to process the next person.

   iteration.resume();
 };
});

The nice thing about adding these as methods on the side is that  
it's completely out of sight in simple scenarios where you may be  
just scanning to build some HTML for example. Only if you're doing  
multiple coordinated, async tasks you need to know about these  
functions.


Regards,
-pablo



The proposed WD can do what you want. You would write the following  
code for async processing, even though the solution is not the easiest  
to follow:


async_db.request.onsuccess = function() {
  var people = async_db.request.result;
  people.request.onsuccess = function() {
var cursor = people.request.result;
var eachPerson = function() {
  var person = cursor.value;
  cursor.request.onsuccess = eachPerson;
  people.request.onsuccess = function() {
var manager = people.request.result;
// do something with both person and manager
cursor.continue();
  }
  cursor.request.onerror = function () {
// do what you will when all the people have been processed
  }
  people.get(person.managerId);
}
eachPerson();
  }
  people.openCursor();
}
async_db.openObjectStore(people);

It is difficult to follow the logic above, so I will also give a  
synchronous example:


var people = async_db.openObjectStore('people');
var cursor = people.openCursor();
if (cursor)
do {
  var person = cursor.value;
  var manager = people.get(person.managerId);
  // do something with person and manager
} while (cursor.continue());
// we are done with all the people

As I said earlier, there is something to be said about the  
comprehensibility of the asynchronous code above, but let's look at  
the expressive power separately from ease of programming.


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