Many organizations make heavy use of message-oriented middleware, such as Oracle AQ. An interesting thing about message buses is that so long as a program can participate in the sending and receiving of messages it doesn’t matter what language it is written in, so I have added the capability to interoperate with AQ to OCI*ML. This proved to be quite tricky, as to get the very high-level interface to it I wanted, I am effectively generating new object-types on the fly, something I don’t think the underlying OCI was really designed for.
On with the code! First, to create an AQ:
create type message_t as object ( message_id integer, message_text varchar2(80)); / begin dbms_aqadm.create_queue_table ( queue_table => 'tbl_message_queue', queue_payload_type => 'message_t'); dbms_aqadm.create_queue ( queue_name => 'message_queue', queue_table => 'tbl_message_queue'); dbms_aqadm.start_queue ( queue_name => 'message_queue'); end; /
This declares a new type message_t
, sets up a table of the type and creates and starts a queue on it. The type definition will be needed to write OCaml to interact correctly with this particular queue. To enqueue a message:
$ ./ocimlsh
# open Ociml;; # let lda = oralogon "gaius/abc123";; # oraenqueue lda "message_queue" "message_t" [|Integer 99; Varchar "hello, world!"|];; # oracommit lda;;
Note the commit
, as this is an Oracle product it works just like a database, similarly when dequeueing one must also commit to confirm receipt of the message. I am specifying the name of the queue, the type of the queue, and an array of OCI*ML types comprising the message body, which matches message_t
declared in the PL/SQL. This can obviously be curried, so only the message needs to be applied. Dequeuing is similar, but requires a “dummy” message so that OCI*ML is able to convert the types it gets back from Oracle correctly:
# oradequeue lda "message_queue" "message_t" [|Integer 0; Varchar ""|];; - : Ociml.col_value array = [|Integer 99; Varchar "hello, world!"|] # oracommit;;
In both cases, significantly less code that it would take to do this in PL/SQL in SQL*Plus.
Performance seems reasonable; using Tibco’s assumption of 50-byte message, I can enqueue/commit at a rate ~100 msgs/sec on a very small system (a 1G Debian VM running XE) , spending ~92% of time waiting on Oracle†, so OCI*ML itself is nowhere close to being the bottleneck, despite no optimization (such as caching the TDO, or native compiling with ocamlopt
) and I would expect this to fly on real hardware.
While I won’t be building a trading engine on OCI*ML quite yet, this is perfectly serviceable for reliable, asynchronous notification and IPC for distributed systems and over WAN links. Next up will be support for more datatypes, particularly Raw. Perhaps after all these years, as an administrator of rather than having a use for it myself, I might reconsider my opinion of AQ…
† Which in turn is spending most of its time on log file sync wait to a slow virtual disk, and autocommit
is not supported for AQ so there is additional latency even over a local connection.
Pingback: OCI*ML: Minor updates | So I decided to take my work back underground
Pingback: OCI*ML: Support for RETURNING | So I decided to take my work back underground
Pingback: OCI*ML: Ref Cursors and Raw Messages | So I decided to take my work back underground
Pingback: OCaml bindings for Coherence with SWIG | So I decided to take my work back underground
Pingback: OCaml binding for Coherence MapListener | So I decided to take my work back underground
Pingback: Custom classes in Coherence with OCaml | So I decided to take my work back underground
Pingback: Putting it all together: PubSub for OCaml with Coherence | So I decided to take my work back underground