Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Using Active Failover in ArangoDB by Configuring ArangoDB-PHP

DZone's Guide to

Using Active Failover in ArangoDB by Configuring ArangoDB-PHP

If you're using PHP and ArangoDB, you'll want to have a look at this post. We take a look at how to setup active failover for your ArangoDB cluster.

· Integration Zone ·
Free Resource
CRM integration has become the cornerstone to meeting initiatives across organizations. Explore the top 6 value-driven Salesforce CRM integrations ebook.  

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.

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!

Sync, automate, and notify lead to customer changes across marketing, CRM, and messaging apps in real-time with the Cloud Elements eventing framework. Learn more.

Topics:
arangodb ,failover ,php ,integration ,database integration

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}