Features from the xrg dungeons

This series of articles aims to guide you through a set of pending features for OpenERP. Some of them are experimental, some more mature, some need to contain their maturing process... (read more)

Tuesday, August 9, 2011

Reverse-RPC

Status: Testing
Conceived: July 2011, based on a2billing 2007 technology
Implemented: July-August 2011
Commit-id: 7ae8b4f81e4aacad at "addons"

The RPC enhancements saga continues...

... now, in the reverse direction: pass commands from the server to the client. Say, why ever need to do that?

Take, for example, the openerp-buildbot. It is an openerp client connected to a command database. But it is a smart, autonomous bot that processes builds, tasks. Sometimes, the OpenERP database needs to send a request to the buildbot, like "please, start the pending builds" or even "reconfigure yourself, my database-stored layout has changed".

We want that a command is sent from the OpenERP server to a connected client, containing a payload
of data (command arguments), and hopefully executed in real time.

Note: this protocol is not really guaranteed to deliver real-time results. It only promises best-effort flow
But, still, we don't want to define a different RPC protocol, a socket opened from the server to the client. We stick with the existing Net-RPC or HTTP implementations, from client to server.

The idea is trivial: we push all requests into a table (ORM model), and let clients pop them, execute and send the result back. This way, we can keep a closely monitored track of execution flow, and keep a asynchronous design. It's the same technology I'd used in A2Billing v2 notification (aka. alarms) feature.
 By using Koo's "subscription" protocol, the clients to that "commands" table can wake in real time and process their pending tasks. Python's meta-programming capabilities also help expose a virtual RPC-like API to both the server (which issues commands) and the client.

Example (at the server, taken from buildbot):

bc_obj = self.pool.get('base.command.address')
proxy = bc_obj.get_proxy(cr, uid, 'software_dev.buildbot:%d' % (bbid))
proxy.triggerMasterRequests()
Client code:
class MasterPoller:

   @call_with_master
   def triggerMasterRequests(self, master):
        d = master.pollDatabaseBuildRequests()
        return d

No comments:

Post a Comment