Current status

This module is still in an experimental phase.

Module Description

This module is a backend of mod_event_pusher that enables support for the RabbitMQ integration. Currently there are 5 available notifications:

  • user presence changed - Carries the user id (full jid by default) and a boolean field corresponding to the current user online status.
  • private message sent/received - Carries the user ids (both sender and receiver) along with the message body.
  • group message sent/received - Carries the user id and the room id (full jids by default) along with the message body.

All these notifications are sent as JSON strings to RabbitMQ exchanges. Type of exchanges can be chosen as desired. Each type of the notifications is sent to its dedicated exchange. There are three exchanges created on startup of the module, for presences, private messages and group chat messages related events.

Messages are published to a RabbitMQ server with routing key being set to a user bare jid (user@domain) and configurable topic e.g alice@localhost.private_message_sent.

The module requires rabbit pool of AMQP connections to be configured in order to make the module work. It's well advised to read through Advanced configuration/Outgoing connections section before enabling the module.

Options

  • presence_exchange - Defines presence exchange options, such as:
  • name - (string, default: <<"presence">>) - Defines RabbitMQ presence exchange name;
  • type (string, default: <<"topic">>) - Defines RabbitMQ presence exchange type;
  • chat_msg_exchange - Defines chat message exchange options, such as:
  • name - (string, default: <<"chat_msg">>) - Defines RabbitMQ chat message exchange name;
  • type (string, default: <<"topic">>) - Defines RabbitMQ chat message exchange type;
  • sent_topic - (string, default: <<"chat_msg_sent">>) - Defines RabbitMQ chat message sent topic name;
  • recv_topic - (string, default: <<"chat_msg_recv">>) - Defines RabbitMQ chat message received topic name;
  • groupchat_msg_exchange - Defines group chat message exchange options, such as:
  • name - (string, default: <<"groupchat_msg">>) - Defines RabbitMQ group chat message exchange name;
  • type (string, default: <<"topic">>) - Defines RabbitMQ group chat message exchange type;
  • sent_topic (string, default: <<"groupchat_msg_sent">>) - Defines RabbitMQ group chat message sent topic name;
  • recv_topic (string, default: <<"groupchat_msg_recv">>) - Defines RabbitMQ group chat message received topic name;

Example configuration

{mod_event_pusher, [
    {backends, [
        {rabbit, [
            {presence_exchange, [{name, <<"presence">>},
                                 {type, <<"topic">>}]},
            {chat_msg_exchange, [{name, <<"chat_msg">>},
                                 {sent_topic, <<"chat_msg_sent">>},
                                 {recv_topic, <<"chat_msg_recv">>}]},
            {groupchat_msg_exchange, [{name, <<"groupchat_msg">>},
                                      {sent_topic, <<"groupchat_msg_sent">>},
                                      {recv_topic, <<"groupchat_msg_recv">>}]}
        ]}
    ]}
]}

JSON Schema examples

The different kinds of notifications deliver slightly different messages. The messages are delivered in a JSON format.

Presence updates

The JSON format for an online presence update notification is:

{
    "user_id": "alice@localhost/res1",
    "present": true
}

For offline presence updates, the present boolean value is set to false:

{
    "user_id": "alice@localhost/res1",
    "present": false
}

Sent/received messages

The JSON format for a private message notification is:

{
    "to_user_id": "bob@localhost/res1",
    "message": "Hello, Bob",
    "from_user_id": "alice@localhost/res1"
}

The notification is similar for group messages. For example for "sent" events:

{
    "to_user_id": "muc_publish@muc.localhost",
    "message": "Hi, Everyone!",
    "from_user_id": "bob@localhost/res1"
}

and for "received" events:

{
    "to_user_id": "bob@localhost/res1",
    "message": "Hi, Everyone!",
    "from_user_id": "muc_publish@muc.localhost/alice"
}

Metrics

The module provides some metrics related to RabbitMQ connections and messages as well. Provided metrics:

name type description (when it gets incremented/decremented)
[Host, connections_active] spiral A connection to a RabbitMQ server is opened(+1)/closed(-1).
[Host, connections_opened] spiral A connection to a RabbitMQ server is opened.
[Host, connections_closed] spiral A connection to a RabbitMQ server is closed.
[Host, connection_failed ] spiral A try to open a connection to a RabbitMQ server failed.
[Host, messages_published] spiral A message to a RabbitMQ server is published.
[Host, messages_failed] spiral A message to a RabbitMQ server is rejected.
[Host, messages_timeout] spiral A message to a RabbitMQ server timed out (weren't confirmed by the server).
[Host, message_publish_time] histogram Amount of time it takes to publish a message to a RabbitMQ server and receive a confirmation. It's measured only for successful messages.
[Host, message_payload_size] histogram Size of a message (in bytes) that was published to a RabbitMQ server (including message properties). It's measured only for successful messages.

All the above metrics have a prefix which looks as follows:
<xmpp_host>.backends.mod_event_pusher_rabbit.<metric_name>. For example a proper metric name would look like: localhost.backends.mod_event_pusher_rabbit.connections_active

Guarantees

There are no guarantees. The current implementation uses "best effort" approach which means that we don't care if a message is delivered to a RabbitMQ server. If publisher confirms are enabled and a message couldn't be delivered to the server for some reason (the server sent negative acknowledgment/didn't sent it at all or there was a channel exception) the module just updates appropriate metrics and prints some log messages. Notice that there might be situations when a message silently gets lost.

Type of exchanges

By default all the exchanges used are of type topic. Using topic exchanges gives a lot of flexibility when binding queues to such an exchange by using # and * in binding keys. But flexibility comes at the cost of performance - imagine a scenario where there are thousands of users and AMQP consumers use binding keys for particular users which look like user_N@host.#. In such case RabbitMQ has to go through all the users in order to find out where a message should be sent to. This operations is proved to be costly. In a load test with 100k users a delay caused by this operation was substantial (about an order of magnitude higher than compared to a load test with 60k users).

If performance is a top priority go for direct exchanges. Using this type of exchanges is proved to work efficiently with 100k users. Keep in mind it gives up flexibility over performance.

Publisher confirms

By default publisher confirmations are disabled. However, one-to-one confirmations can be enabled (see RabbitMQ connection setup section). When a worker sends a message to a RabbitMQ server it waits for a confirmation from the server before it starts to process next message. This approach allows to introduce backpressure on a RabbitMQ server connection cause the server can reject/not confirm messages when it's overloaded. On the other hand it can cause performance degradation.

Worker selection strategy

The module uses mongoose_wpool for managing worker processes and best_worker strategy, for choosing a worker, is in use by default. Different strategies imply different behaviors of the system.

Event messages queuing

When available_worker strategy is in use all the event messages are queued in single worker pool manager process state. When different strategy is set e.g best_worker those messages are placed in worker processes inboxes. Worker selection strategy can be set in rabbit pool configuration.

Event messages ordering

None of worker selection strategies ensures that user events will be delivered to a RabbitMQ server properly ordered in time.