# Active-passive Messaging Clusters (Preview)

## Overview

This release provides a preview of a new module for High Availability (HA). The new module is not yet complete or ready for production use. It being made available so that users can experiment with the new approach and provide feedback early in the development process. Feedback should go to dev@qpid.apache.org.

The old cluster module takes an active-active approach, i.e. all the brokers in a cluster are able to handle client requests simultaneously. The new HA module takes an active-passive, hot-standby approach.

In an active-passive cluster only one broker, known as the primary, is active and serving clients at a time. The other brokers are standing by as backups. Changes on the primary are immediately replicated to all the backups so they are always up-to-date or "hot". If the primary fails, one of the backups is promoted to take over as the new primary. Clients fail-over to the new primary automatically. If there are multiple backups, the backups also fail-over to become backups of the new primary. Backup brokers reject connection attempts, to enforce the requirement that only the primary be active.

This approach depends on an external cluster resource manager to detect failures and choose the primary. Rgmanager is supported initially, but others may be supported in the future.

### Why the new approach?

The new active-passive approach has several advantages compared to the existing active-active cluster module.

- It does not depend directly on openais or corosync. It does not use multicast which simplifies deployment.
- It is more portable: in environments that don't support corosync, it can be integrated with a resource manager available in that environment.
- Replication to a disaster recovery site can be handled as simply another node in the cluster, it does not require a separate replication mechanism.
- It can take advantage of features provided by the resource manager, for example virtual IP addresses.
- Improved performance and scalability due to better use of multiple CPU s

### Limitations

There are a number of known limitations in the current preview implementation. These will be fixed in the production versions.

- Transactional changes to queue state are not replicated atomically. If the primary crashes during a transaction, it is possible that the backup could contain only part of the changes introduced by a transaction.
- During a fail-over one backup is promoted to primary and any other backups switch to the new primary. Messages sent to the new primary before all the backups have switched could be lost if the new primary itself fails before all the backups have switched.
- Acknowledgments are confirmed to clients before the message has been dequeued from replicas or indeed from the local store if that is asynchronous.
- When used with a persistent store: if the entire cluster fails, there are no tools to help identify the most recent store.
- A persistent broker must have its store erased before joining an existing cluster. In the production version a persistent broker will be able to load its store and avoid downloading messages that are in the store from the primary.
- Configuration changes (creating or deleting queues, exchanges and bindings) are replicated asynchronously. Management tools used to make changes will consider the change complete when it is complete on the primary, it may not yet be replicated to all the backups.
- Deletions made immediately after a failure (before all the backups are ready) may be lost on a backup. Queues, exchange or bindings that were deleted on the primary could re-appear if that backup is promoted to primary on a subsequent failure.
- Better control is needed over which queues/exchanges are replicated and which are not.
- There are some known issues affecting performance, both the throughput of replication and the time taken for backups to fail-over. Performance will improve in the production version.
- Federated links from the primary will be lost in fail over, they will not be re-connected on the new primary. Federation links to the primary can fail over.
- Only plain FIFO queues can be replicated. LVQ and ring queues are not yet supported.

## Virtual IP Addresses

Some resource managers (including `rgmanager`) support virtual IP addresses. A virtual IP address is an IP address that can be relocated to any of the nodes in a cluster. The resource manager associates this address with the primary node in the cluster, and relocates it to the new primary when there is a failure. This simplifies configuration as you can publish a single IP address rather than a list.

A virtual IP address can be used by clients and backup brokers to connect to the primary. The following sections will explain how to configure virtual IP addresses for clients or brokers.

## Configuring the Brokers

The broker must load the `ha` module, it is loaded by default. The following broker options are available for the HA module.

Options for High Availability Messaging Cluster

Options for High Availability Messaging Cluster
`--ha-cluster yes|no` | Set to "yes" to have the broker join a cluster.
`--ha-brokers-url URL` | A URL listing each broker in the cluster. The full format of the URL is given by this grammar:
    		    url = ["amqp:"][ user ["/" password] "@" ] addr ("," addr)*
    		    addr = tcp_addr / rmda_addr / ssl_addr / ...
    		    tcp_addr = ["tcp:"] host [":" port]
    		    rdma_addr = "rdma:" host [":" port]
    		    ssl_addr = "ssl:" host [":" port]'
It is used by brokers to connect to each other. This URL must explicitly list each broker, it cannot be a virtual IP address. For example `amqp:node1.exaple.com,node2.exaple.com,node3.exaple.com`
`--ha-public-url URL` | The URL that is advertized to clients. This has the same format as the `--ha-brokers-url` URL above. This URL can contain a list of all the brokers' addresses or a single virtual IP address. A virtual IP address is recommended as it simplifies deployment and hides changes to the cluster membership from clients. You can use this option to put client traffic on a different network from broker traffic, which is recommended.
`--ha-replicate` | Specifies whether queues and exchanges are replicated by default. For details see
`--ha-username USER` `--ha-password PASS` `--ha-mechanism MECH` | Authentication settings used by brokers to connect to each other.

To configure a HA cluster you must set at least `ha-cluster` and `ha-brokers-url`.

## The Cluster Resource Manager

Broker fail-over is managed by a cluster resource manager. An integration with rgmanager is provided, but it is possible to integrate with other resource managers.

The resource manager is responsible for starting the `qpidd` broker on each node in the cluster. The resource manager promotes one of the brokers to be the primary. The other brokers connect to the primary as backups, using the URL provided in the `ha-brokers-url` configuration option.

Once connected, the backup brokers synchronize their state with the primary. When a backup is synchronized, or "hot", it is ready to take over if the primary fails. Backup brokers continually receive updates from the primary in order to stay synchronized.

If the primary fails, backup brokers go into fail-over mode. The resource manager must detect the failure and promote one of the backups to be the new primary. The other backups connect to the new primary and synchronize their state with it.

The resource manager is also responsible for protecting the cluster from split-brain conditions resulting from a network partition. A network partition divide a cluster into two sub-groups which cannot see each other. Usually a quorum voting algorithm is used that disables nodes in the inquorate sub-group.

## Configuring `rgmanager` as resource manager

This section assumes that you are already familiar with setting up and configuring clustered services using `cman` and `rgmanager`. It will show you how to configure an active-passive, hot-standby `qpidd` HA cluster with `rgmanager`.

You must provide a `cluster.conf` file to configure `cman` and `rgmanager`. Here is an example `cluster.conf` file for a cluster of 3 nodes named node1, node2 and node3. We will go through the configuration step-by-step.

    <?xml version="1.0"?>
    <!--
    This is an example of a cluster.conf file to run qpidd HA under rgmanager.
    This example assumes a 3 node cluster, with nodes named node1, node2 and node3.

    NOTE: fencing is not shown, you must configure fencing appropriately for your cluster.
    -->

    <cluster name="qpid-test" config_version="18">
      <!-- The cluster has 3 nodes. Each has a unique nodid and one vote
           for quorum. -->
      <clusternodes>
        <clusternode name="node1.example.com" nodeid="1"/>
        <clusternode name="node2.example.com" nodeid="2"/>
        <clusternode name="node3.example.com" nodeid="3"/>
      </clusternodes>
      <!-- Resouce Manager configuration. -->
      <rm>
        <!--
    	There is a failoverdomain for each node containing just that node.
    	This lets us stipulate that the qpidd service should always run on each node.
        -->
        <failoverdomains>
          <failoverdomain name="node1-domain" restricted="1">
    	<failoverdomainnode name="node1.example.com"/>
          </failoverdomain>
          <failoverdomain name="node2-domain" restricted="1">
    	<failoverdomainnode name="node2.example.com"/>
          </failoverdomain>
          <failoverdomain name="node3-domain" restricted="1">
    	<failoverdomainnode name="node3.example.com"/>
          </failoverdomain>
        </failoverdomains>

        <resources>
          <!-- This script starts a qpidd broker acting as a backup. -->
          <script file="/etc/init.d/qpidd" name="qpidd"/>

          <!-- This script promotes the qpidd broker on this node to primary. -->
          <script file="/etc/init.d/qpidd-primary" name="qpidd-primary"/>

          <!-- This is a virtual IP address for broker replication traffic. -->
          <ip address="20.0.10.200" monitor_link="1"/>

          <!-- This is a virtual IP address on a seprate network for client traffic. -->
          <ip address="20.0.20.200" monitor_link="1"/>
        </resources>

        <!-- There is a qpidd service on each node, it should be restarted if it fails. -->
        <service name="node1-qpidd-service" domain="node1-domain" recovery="restart">
          <script ref="qpidd"/>
        </service>
        <service name="node2-qpidd-service" domain="node2-domain" recovery="restart">
          <script ref="qpidd"/>
        </service>
        <service name="node3-qpidd-service" domain="node3-domain"  recovery="restart">
          <script ref="qpidd"/>
        </service>

        <!-- There should always be a single qpidd-primary service, it can run on any node. -->
        <service name="qpidd-primary-service" autostart="1" exclusive="0" recovery="relocate">
          <script ref="qpidd-primary"/>
          <!-- The primary has the IP addresses for brokers and clients to connect. -->
          <ip ref="20.0.10.200"/>
          <ip ref="20.0.20.200"/>
        </service>
      </rm>
    </cluster>

There is a `failoverdomain` for each node containing just that one node. This lets us stipulate that the qpidd service should always run on all nodes.

The `resources` section defines the `qpidd` script used to start the `qpidd` service. It also defines the `qpid-primary` script which does not actually start a new service, rather it promotes the existing `qpidd` broker to primary status.

The `resources` section also defines a pair of virtual IP addresses on different sub-nets. One will be used for broker-to-broker communication, the other for client-to-broker.

To take advantage of the virtual IP addresses, `qpidd.conf` should contain these lines:

          ha-cluster=yes
          ha-brokers-url=20.0.20.200
          ha-public-url=20.0.10.200

This configuration specifies that backup brokers will use 20.0.20.200 to connect to the primary and will advertise 20.0.10.200 to clients. Clients should connect to 20.0.10.200.

The `service` section defines 3 `qpidd` services, one for each node. Each service is in a restricted fail-over domain containing just that node, and has the `restart` recovery policy. The effect of this is that rgmanager will run `qpidd` on each node, restarting if it fails.

There is a single `qpidd-primary-service` using the `qpidd-primary` script which is not restricted to a domain and has the `relocate` recovery policy. This means rgmanager will start `qpidd-primary` on one of the nodes when the cluster starts and will relocate it to another node if the original node fails. Running the `qpidd-primary` script does not start a new broker process, it promotes the existing broker to become the primary.

## Broker Administration Tools

Normally, clients are not allowed to connect to a backup broker. However management tools are allowed to connect to a backup brokers. If you use these tools you must not add or remove messages from replicated queues, or delete replicated queues or exchanges as this will corrupt the replication process and may cause message loss.

`qpid-ha` allows you to view and change HA configuration settings.

The tools `qpid-config`, `qpid-route` and `qpid-stat` will connect to a backup if you pass the flag `--ha-admin` on the command line.

## Creating replicated queues and exchanges

By default, queues and exchanges are not replicated automatically. You can change the default behavior by setting the `ha-replicate` configuration option. It has one of the following values:

- all: Replicate everything automatically: queues, exchanges, bindings and messages.
- configuration: Replicate the existence of queues, exchange and bindings but don't replicate messages.
- none: Don't replicate anything, this is the default.

You can over-ride the default for a particular queue or exchange by passing the argument `qpid.replicate` when creating the queue or exchange. It takes the same values as `ha-replicate`

Bindings are automatically replicated if the queue and exchange being bound both have replication `all` or `configuration`, they are not replicated otherwise.

You can create replicated queues and exchanges with the `qpid-config` management tool like this:

          qpid-config add queue myqueue --replicate all

To create replicated queues and exchanges via the client API, add a `node` entry to the address like this:

          "myqueue;{create:always,node:{x-declare:{arguments:{'qpid.replicate':all}}}}"

## Client Connection and Fail-over

Clients can only connect to the primary broker. Backup brokers automatically reject any connection attempt by a client.

Clients are configured with the URL for the cluster (details below for each type of client). There are two possibilities

- The URL contains multiple addresses, one for each broker in the cluster.
- The URL contains a single virtual IP address that is assigned to the primary broker by the resource manager.

  Only if the resource manager supports virtual IP addresses

In the first case, clients will repeatedly re-try each address in the URL until they successfully connect to the primary. In the second case the resource manager will assign the virtual IP address to the primary broker, so clients only need to re-try on a single address.

When the primary broker fails, clients re-try all known cluster addresses until they connect to the new primary. The client re-sends any messages that were previously sent but not acknowledged by the broker at the time of the failure. Similarly messages that have been sent by the broker, but not acknowledged by the client, are re-queued.

TCP can be slow to detect connection failures. A client can configure a connection to use a heartbeat to detect connection failure, and can specify a time interval for the heartbeat. If heartbeats are in use, failures will be detected no later than twice the heartbeat interval. The following sections explain how to enable heartbeat in each client.

See "Cluster Failover" in Programming in Apache Qpid for details on how to keep the client aware of cluster membership.

Suppose your cluster has 3 nodes: `node1`, `node2` and `node3` all using the default AMQP port. To connect a client you need to specify the address(es) and set the `reconnect` property to `true`. Here's how to connect each type of client:

### C++ clients

With the C++ client, you specify multiple cluster addresses in a single URL

The full grammar for the URL is:

    	    url = ["amqp:"][ user ["/" password] "@" ] addr ("," addr)*
    	    addr = tcp_addr / rmda_addr / ssl_addr / ...
    	    tcp_addr = ["tcp:"] host [":" port]
    	    rdma_addr = "rdma:" host [":" port]
    	    ssl_addr = "ssl:" host [":" port]'

You also need to specify the connection option `reconnect` to be true. For example:

    	qpid::messaging::Connection c("node1,node2,node3","{reconnect:true}");

Heartbeats are disabled by default. You can enable them by specifying a heartbeat interval (in seconds) for the connection via the `heartbeat` option. For example:

    	  qpid::messaging::Connection c("node1,node2,node3","{reconnect:true,heartbeat:10}");

### Python clients

With the python client, you specify `reconnect=True` and a list of host:port addresses as `reconnect_urls` when calling `Connection.establish` or `Connection.open`

    	connection = qpid.messaging.Connection.establish("node1", reconnect=True, reconnect_urls=["node1", "node2", "node3"])

Heartbeats are disabled by default. You can enable them by specifying a heartbeat interval (in seconds) for the connection via the 'heartbeat' option. For example:

    	connection = qpid.messaging.Connection.establish("node1", reconnect=True, reconnect_urls=["node1", "node2", "node3"], heartbeat=10)

### Java JMS Clients

In Java JMS clients, client fail-over is handled automatically if it is enabled in the connection. You can configure a connection to use fail-over using the `failover` property:

    	connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'&failover='failover_exchange'

This property can take three values:

Fail-over Modes

failover_exchange

  If the connection fails, fail over to any other broker in the cluster.

roundrobin

  If the connection fails, fail over to one of the brokers specified in the `brokerlist`.

singlebroker

  Fail-over is not supported; the connection is to a single broker only.

In a Connection URL, heartbeat is set using the `idle_timeout` property, which is an integer corresponding to the heartbeat period in seconds. For instance, the following line from a JNDI properties file sets the heartbeat time out to 3 seconds:

    	connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672',idle_timeout=3