home shape

Configuring ArangoDB-PHP to use active failover

This article is about setting up active failover for ArangoDB-PHP, the PHP client driver for ArangoDB. It requires ArangoDB-PHP 3.3.2 or higher, and an ArangoDB server version of 3.3.4 or higher.

Interested in trying out ArangoDB? Fire up your cluster in just a few clicks with ArangoDB ArangoGraph: the Cloud Service for ArangoDB. Start your free 14-day trial here

Active failover: basic setup

Historically, ArangoDB-PHP has been able to connect to a single ArangoDB endpoint, i.e. one combination of IP address and port number.

To connect to an ArangoDB server that is running on localhost or on a remote server, simply set the `OPTION_ENDPOINT` item in the `ConnectionOptions` and connect:

php
namespace ArangoDBClient;

// following can be omitted when composer.json is used to set up the project
require __DIR__ . DIRECTORY_SEPARATOR . 'autoload.php';

$connectionOptions = [
  ConnectionOptions::OPTION_ENDPOINT                => 'tcp://127.0.0.1:8529',
  ConnectionOptions::OPTION_AUTH_USER               => 'root',
  ConnectionOptions::OPTION_AUTH_PASSWD             => ''
];

$connection = new Connection($connectionOptions);
$connection->test(); // should return true if connection works

To make use of the new active failover option in ArangoDB 3.3, the PHP driver now allows setting up multiple endpoints. For example, if the active failover ArangoDB instances run on 127.0.0.1 on ports 8529, 8539 and 8549, the example configuration would be:

php
$connectionOptions = [
  ConnectionOptions::OPTION_ENDPOINT                => [ 
    'tcp://127.0.0.1:8529',
    'tcp://127.0.0.1:8539',
    'tcp://127.0.0.1:8549'
  ],
  ConnectionOptions::OPTION_AUTH_USER               => 'root',
  ConnectionOptions::OPTION_AUTH_PASSWD             => ''
];

(Please note that in reality the ArangoDB instances should be set up on different physical machines. We only use the same machine here for demonstration purposes.)

The driver will by default try to connect to the first endpoint in the endpoints array, and only try the following servers if no connection can be established. If no connection can be made to any of the configured endpoints, the driver will bail out by throwing an exception.

If the driver connects to a follower (non-leader), it would not be able to perform any write operations on it, so the follower will reject the connection and redirect to the current leader. The driver will then automatically reconnect to the current leader.

In case the leader has just failed and the failover is currently ongoing, there will be a “leadership challenge” period in which the remaining followers will try to compete for leadership. In this period the driver will try to find the new leader for a configurable amount of time, which can be adjusted using the `OPTION_FAILOVER_TIMEOUT` options entry. The maximum number of servers to contact in case of a failover can be configured by setting the `OPTION_FAILOVER_TRIES` entry.

It is also possible to register a user-defined callback function that will be invoked if the failover procedure cannot find any server to contact to (e.g. because all servers are unreachable). This can be set via the `OPTION_NOTIFY_CALLBACK` entry.

With the options all set, we can now try the active failover by starting a few local ArangoDB servers in active failover mode.


arangodb --starter.local --starter.mode=resilientsingle 

By default, this will start three local ArangoDB instances on ports 8529, 8539 and 8549.

We can now use the following PHP code to test connecting to the correct server and automatic failover:

php
$connection = new Connection($connectionOptions);
$collectionHandler = new CollectionHandler($connection);

while (true) {
  $collectionHandler->getAllCollections();
  print 'successfully fetched list of collections from endpoint: ' . $connection->getCurrentEndpoint() . PHP_EOL;

  sleep(1);
}

The above code will automatically connect to the configured servers, starting with the first one in the endpoints array, until it finds the leader.

The script will run endlessly and print to which server it is connected to. One can now viciously kill the current leader by sending it a kill signal from another shell. The script will then show that the driver has automatically connected to one of the remaining servers that, in its turn, became a new leader.

Using Memcached for storing the last known leader

As it is unknown to the driver on startup which server from the endpoints array is the current leader, the driver will connect to the specified servers in array order by default. To spare a few unnecessary connection attempts to non-leaders (or failed servers), it is possible to set up caching (using Memcached) for the server list. The cached server list will contain the last working server first, so that as few connection attempts as possible will need to be made for future invocations.

In order to use caching, it is required to install the Memcached module for PHP, and to set up the following relevant options in the `ConnectionOptions`:

php
$connectionOptions = [
  // memcached persistent id (will be passed to Memcached::__construct)
  ConnectionOptions::OPTION_MEMCACHED_PERSISTENT_ID => 'arangodb-php-pool',

  // memcached servers to connect to (will be passed to Memcached::addServers)
  ConnectionOptions::OPTION_MEMCACHED_SERVERS       => [ [ '127.0.0.1', 11211 ] ],

  // memcached options (will be passed to Memcached::setOptions)
  ConnectionOptions::OPTION_MEMCACHED_OPTIONS       => [ ],

  // key to store the current endpoints array under
  ConnectionOptions::OPTION_MEMCACHED_ENDPOINTS_KEY => 'arangodb-php-endpoints',

  // time-to-live for the endpoints array stored in memcached
  ConnectionOptions::OPTION_MEMCACHED_TTL           => 600
];

Full setup example

Putting all the above together, we end up with the following PHP code for setting up the active failover:

php
namespace ArangoDBClient;

// following can be omitted when composer.json is used to set up the project
require __DIR__ . DIRECTORY_SEPARATOR . 'autoload.php';

$connectionOptions = [
  ConnectionOptions::OPTION_ENDPOINT                => [ 
    'tcp://127.0.0.1:8529',
    'tcp://127.0.0.1:8539',
    'tcp://127.0.0.1:8549'
  ],
  ConnectionOptions::OPTION_AUTH_USER               => 'root',
  ConnectionOptions::OPTION_AUTH_PASSWD             => '',

  ConnectionOptions::OPTION_FAILOVER_TIMEOUT        => 30,
  ConnectionOptions::OPTION_NOTIFY_CALLBACK         => function($message) { 
                                                         print 'Oops, failover error: ' . $message . PHP_EOL; 
                                                       },

  ConnectionOptions::OPTION_MEMCACHED_PERSISTENT_ID => 'arangodb-php-pool',
  ConnectionOptions::OPTION_MEMCACHED_SERVERS       => [ [ '127.0.0.1', 11211 ] ],
  ConnectionOptions::OPTION_MEMCACHED_ENDPOINTS_KEY => 'arangodb-php-endpoints',
  ConnectionOptions::OPTION_MEMCACHED_TTL           => 600
];

$connection = new Connection($connectionOptions);
$collectionHandler = new CollectionHandler($connection);

while (true) {
  $collectionHandler->getAllCollections();
  print 'successfully fetched list of collections from endpoint: ' . $connection->getCurrentEndpoint() . PHP_EOL;

  sleep(1);
}

You can give it a try by downloading the latest maintenance release – ArangoDB 3.3.4, and the ArangoDB-PHP driver. Read more on active failover architecture in our docs.

Enjoy!

Jan Steemann

Jan Steemann

After more than 30 years of playing around with 8 bit computers, assembler and scripting languages, Jan decided to move on to work in database engineering. Jan is now a senior C/C++ developer with the ArangoDB core team, being there from version 0.1. He is mostly working on performance optimization, storage engines and the querying functionality. He also wrote most of AQL (ArangoDB’s query language).

Leave a Comment





Get the latest tutorials, blog posts and news: