Queue : A Queues is basically a list of objects which can be accessed in the same order in which they are put. This is called a FIFO , for first in first out. In caravan an object of type queue is proovided which is persistent (meaninng it will not vanish if server is shut down) . The queues are used to schedule tasks that need to be done sequencially. For example, take the sitauation where a lot of messages have to be send from one server to many other sites. The task which creates the message usaully does not want to wait till the message is dispatched to the destination. Dispatching the message can be done in a separate thread. This thread can poll for any new messages and send them one by one. But in case there are no messages it still needs to poll for new messages. This is an unneccassary wastage of CPU resources. The queue mechanism along with the concept of event handler provides an elegant solution for such situations. By creating a queue and the corresponding eventhandler it is possible to dispatch tasks that are sporadic. Whenever a item is inserted into the queue, the corresponding evendhandler is triggered and starts dispatching the queued task. The best thing is that the what is inserted into the the queue and how the task is handled is totally programable. A Queue is identified by its name and optionally a type. This is because usually a particular task is handled depending on some information that distinguishes it from other tasks -- eaxample, each message is distinguished by the destination address though the logic of sending the message is the same. So in way one can think of the queue name to identify the method of dispatching the task and the queue type to be a parameter to distinguish individual tasks. A queue object is used to create the queues of any name and type. A queue object is also used to access an existing queue and manipulate and view its status. syntax : queue objectname(name,[type]);// type is optional Where name and type are either strings or variable data. Examples decaring a queue object x: 1.queue x(myq,yahoo); // the queue name is myq and type is yahoo 2.queue x(y(name),y(typ));// the name and type are variable 3.queue x(myq);// the name is qname and applicable to all types 4.queue x(myq,*);// the name is qname and applicable to all types Queue properties: total : total items in queue name : name of the queue queueid : identifier for the queue entry Since queue is a list, each entry is identified by its queueid item : content of the queue entry This is the content of the entry, it can be a string or a file type : type of the queue entry priority: priority of the queue entry In caravan queues the item is not only in a fifo but also prioritised, this is a number and lower the value higher the priority, default is '0' resume: wakes up the corresponding eventhandler that had supended itself using the 'suspend' statement. eventhandler : Event handler is a piece of caravan code that is linked to queue very closely. It is created by adding a eventhandler statement in a caravan template which is written to dispatch the queueed tasks. The eventhandler statement is almost identical to the queue declaration syntax. syntax: eventhandler(myq);// handle all tasks with name myq eventhandler(myq,*);//handle all tasks with name myq eventhandler(myq,?);// handle all tasks with name myq, // all types, concurrently for each type eventhandler(myq,mtyp) We will illustrate the use of queue and eventhandler through an example. ========================================================================= Assume we have server recieving information from post operations coming from users and other servers. The job of the server is to store this data in folders depending on the catagory information in the form. The form consists of 'message', 'catagory'' and 'forwardto' . The message is some file of unknown size, which needs to be saved to some folder depending on the 'catagory' and the 'forwardto' are a list of servers which should also be updated with this data. We have a data base called messages which contain three tables 1. files: Holds the data on the local serrver. This is updated every time a form is accepted by our server. 2. catagories: holds category and folder information Contains two fields catagory : a string to denote a the category -- say news, graphics, messages folders : a list of folders in which this catagory has to be stored 3. servers: Contains server information. servername, and ipaddress and port The task is to accept the form, update our table 'messages.files', save the message into its respective folder and forward it to the indicated servers. We can write the code to in such a way that all the above are done in one process. The drawback is that each thread will take an undefined amount of time and if there are many simultaneous requests everything will slow down to a crawl. The code for doing it this way is given below. Code for 'accept_message.html' // this code is executed when a form is posted to caravan. // first put the data into the 'files' table table store=messages.files store(message)=form(message) store(category)=form(category) store(forwardto)=form(forwardto) store(insert) // Now save the message in our folder table cat=messages.categories select from cat where category={category} if cat(selected)> "0" ;// yes we have identified soe categories loop cloop (cat(selected)) ;// there can be multiple folders in which we have to save folder df=cat(folder) df(file)=form(message) cat(nextrecord) repeat cloop endif // now send to other servers loop dloop (form(forwardto(00))) table servers=messages.servers var sql sql(servname)=form(forwardto(dloop(count))) select from servers where servername={servname} if servers(selected)="1";// ideally only there should only be one entry per server form myform myform(_server)=servers(ipaddress) myform(_port)=server(port) myform(_url)="accept_message.html" myform(message)=form(message) myform(category)=form(category) myform(post) endif repeat dloop // now send reply to the client who posted "OK : recvd message" The above code is a simplified form of a communication process. We can see that each part of the code can take much time to complete and the sender of the original form has to wait an undefined amount of time to get the response 'Ok : recvd message'. Now we will split the task into three parts : 1. Receive data: 'accept_message.html' // this code is executed when a form is posted to caravan. // first put the data into the 'files' table table store=messages.files store(message)=form(message) store(category)=form(category) store(forwardto)=form(forwardto) store(insert) // Now queue the task queue saveq("save") ;// a queue is created for saving the data into folders saveq(item)=store(recordno) queue sendq("send") ;// another queue is created to send the data to downstream servers sendq(type)=store(forwardto) sendq(item)=store(recordno) "OK : recvd message" ;// reply ok to sender In the above code all parts of the task is not completed, we have just updated our database and replied 'ok' to sender. The other parts of the task are done by eventhandlers. We have used the queue object to create two events which will trigger the respctive eventhandlers. Event handler for saving the message in local file system:'save.html' _eventhandler ("save");// this says that the follwing code is to be executed when a 'save' event is generated. // there is a queue object '_event' which is already created when this eventhandler is triggered which // is equivalent to the decaration: // queue _event("save") table store=messages.files // retrieve the item store(recordno)=_event(item) ;// now we are pointing to the record which contains the message // rest of the code is similar to what we did before except that now we dont have the original // object 'form' which was posted to us -- we have to get the message from our table table cat=messages.categories var sql sql(category)=store(category) select from cat where category={category} if cat(selected)> "0" ;// yes we have identified some categories loop cloop (cat(selected)) ;// there can be multiple folders in which we have to save folder df=cat(folder) df(file)=store(message) cat(nextrecord) repeat cloop endif _event(deleteitem);// done with this item, delete it // handler will restart with next item if any or gets suspended till another event occurs // if no event occurs for a long time the thread dies and frees up the resources For every message received the above code is executed and the message is saved to its required folder without delaying our client. Note that by not specifying any 'type' for the eventhandler we are ensuring that the messages are processed in the order they have landed, in a sequential manner. Now we need a event handler to send the messages to other servers. Here we will demostrate a slightly different way of handling this task. Since a single message may have to go to multiple servers we will need to send them concurrently -- else all servers may have to face unacceptable delay. Event handler for sending the message to a downstream server:'send.html' ==================================================================== _eventhandler(send,?);// create an instance of this eventhandler for each type // there is a queue object '_event' which is already created when this eventhandler is triggered which // is quivalenent to the decaration: // queue _event("send",servername) // the 'type' here is the servername table store=messages.files // retrieve the item store(recordno)=_event(item) ;// now we are pointing to the record which contains the message table store=messages.files store(recordno)=_event(item) table servers=messages.servers var sql sql(servname)=_event(type) select from servers where servername={servname} if servers(selected)="1";// ideally only there should only be one entry per server form myform myform(_server)=servers(ipaddress) myform(_port)=server(port) myform(_url)="accept_message.html" myform(message)=form(message) myform(category)=form(category) myform(post) if _reply(error);// if there was error set a delay of 300 seconds before trying again. _event(wait)="300" goto skipdelete;// dont delete it now endif endif _event(deleteitem);// delete this item from 'send' queue label skipdelete The above code illustrates how caravan can use the multithreading features of the OS to speed up the response time and performance of your application. This way the message is send to one server per thread. So many threads can become active at once and finish the job much faster. Also see : Tables, form object and loop statements.