16 November 2010 at 11:33 New backend for the ocamlmq STOMP MQ
Shortly after publishing the ocamlmq STOMP message broker, I wrote a sqlite3 backend that I have been improved as of late, to the point that it is faster and more predictable in its performance. You can find ocamlmq 0.5.0a here (git repos here). It retains all the scalability characteristics of the PostgreSQL backend, and improves on its performance, making ocamlmq an attractive solution if you need a fast, small footprint STOMP broker that can scale to millions of queues/messages/subscriptions, supports message priorities and timeouts (useful for queueing jobs) and provides broadcast semantics (topic subscriptions) with prefix matching.
ocamlmq's current 1500 lines of code give a lot of bang per buck: it can queue and deliver messages synchronously about as fast as (or a bit faster than, on my box) beanstalkd, as well as accept messages asynchronously as quickly as and broadcast topic messages faster than RabbitMQ. Also, since it takes 3 MB mem at startup, you can cache many messages in memory before reaching the >140 MB ActiveMQ needs only to boot :)
The new message store takes a full 350 LoCs (vs. <150 for the PostgreSQL), but they are well spent.
Messages are cached in memory and flushed to the permanent sqlite3 store (with full fsync) when either:
more than a given number of messages have been received since the last flush or
it's been more than DT seconds since the last flush
Moreover, messages kept in mem can also be written to a binary log as they are received before they are flushed (and fsync'ed) to the permanent store. This way, in-mem (not yet flushed) messages can be recovered if ocamlmq were killed or crashed. You can additionally ensure that the binlog is fsync'ed after each write. This brings you the strongest durability guarantees, for every single message is fsync'ed before the broker acknowledges its reception.W
The PostgreSQL backend used to slow down if there were lots of delivered, yet non-ACKed messages, and disconnection of a client with many non-ACKed messages resulted in CPU peaks, as all the delivered messages were requeued at once. This is no longer a problem in the improved sqlite3 backend.
Here follow some figures, taken on an oldish dual core AMD Athlon 64 X2 with a 7200 RPM SATA disk. CPU usage at the maximum transfer rates lies between 25 and 50% of one core.
Messages can be queued asynchronously (i.e., either with no receipt or with a receipt sent before the message is saved) at a rate exceeding 60000 msg/s.
Queueing (synchronously, with receipt)
msgmsgs is the number of messages kept in mem before a hard flush.
maxmsgs throughput throughput (-binlog) ---------------------------------------------------- 1000 >11000 msg/s >9000 msg/s 5000 >12000 msg/s >9000 msg/s "infty" >17000 msg/s >14000 msg/s
If the binlog is enabled and -sync-binlog in use, the maximum throughput is around 4000 msg/s.
Topic messages: >40000 msg/s (CPU usage: ~60%) Queue messages in memory: >12000 msg/s Queue messages on disk: >2000 msg/s (~2500 msg/s with sqlite3's WAL mode)