5 Replication tutorial

This article uses the replication information given in the manual to give an example on how to set up a simple replicating system based on an existing single server application. In this example use case the specific goal of the replicating is to improve performance by duplicating the application (+ federation) on different hosts.

This article only applies to X-Hive/DB release 6 and up.

5.1 Replication setup

The first step is to prepare the databases for replication. We create a temporary segment in each database, so that temporary results can be created in the replica-databases. This can be done in the adminclient, by connecting to a database and:

It can also be done in Java code, with something like:
  // Creating a temporary segment in Java code
  session.getDatabase().createTemporaryDataSegment("tempSegmentName", null, 0);
  session.getDatabase().setTemporaryDataSegment("tempSegmentName");

Next, the actual replica is made, assuming the master server is running a page server accessible from other hosts, you can run the following command on the replica host (assuming X-Hive/DB is installed there too):

  XHCreateReplica -debug -federation xhive://masterhost:1235 -replicaid newOrExistingReplicaId 
    -replicabootstrappath /path/to/new/replicadata/XhiveDatabase.bootstrap SuperUserPassword
(XHCreateReplica is not much more than a wrapper around the XhiveFederationIf.registerReplicator(...) and XhiveFederationIf.replicateFully(...) API calls). This will register the replica in the master federation, and copy the complete federation over to the replica host. You can now connect to the federation copy on the replica host, although it is not wise to do so directly (since it should only be accessed as a replica). If something would happen to your replica for whatever reason, you can always remove the files on the replica host and issue this command again.

5.2 Replication application code

On the master host, you can access the federation in the normal way, as long as you make sure it acts as a page server to the replicating host. So assuming that for performance you access the database files directly the driver initialization code in your application would be:

  XhiveDriverIf driver = XhiveDriverFactory.getDriver("/path/to/XhiveDatabase.bootstrap");
  driver.init(numCachePages);
  driver.startListenerThread(new ServerSocket(1235));
(Of course, in a real use situation you want really one code-base for your application, with a configuration switch to indicate whether the machine is acting as a replica or master, with corresponding driver configuration and usage code, but that is out of the scope of this tutorial)

The driver configuration code on the replica host depends on the application. If you only want to run read-only transactions on your replica federation only an extra call to XhiveDriverIf.configureReplicator(...) is needed. However, for our example we want the replica host to act as a full copy of the application, including allowing for read-write transactions. Since X-Hive/DB replication only allows for updates on the master copy of the federation, this means some extra configuration is needed on the replica to allow for update transactions. Specifically, on the replica a distinction is made between readonly transactions (which can act on the replication federation, and thus can have direct access to (a copy of) the database files), and update transactions which have to access the master federation. This results in much slower performance for these update transactions (relative to readonly transactions on the replica or to updates on the master host itself), so this setup is only advised if the majority of your application's transactions are readonly transactions.

In code, this means you have to create two driver objects, one that acts on the replica and one that connects to the master-server directly:

  // On replica-host, use two drivers
  XhiveDriverIf masterDriver = XhiveDriverFactory.getDriver("xhive://masterhost:1235");
  masterDriver.init(numCachePages / 2);
  XhiveDriverIf replicaDriver = XhiveDriverFactory.getDriver("/path/to/replicadir/XhiveDatabase.bootstrap");
  driver.configureReplicator("xhive://masterhost:1235", "previouslySetupId");
  replicaDriver.init(numCachePages / 2);

In your session pooling code, you decide which driver to get the session from based on whether it is a readonly transaction or not. For performance, it is advisable to have your session pool code (and the code that uses the session pool) already make a distinction between readonly and readwrite transactions (using XhiveSessionIf.setReadOnlyMode(...)) for concurrency performance. Once you have that, this could be the getSession code:

  synchronized XhiveSessionIf getSession(boolean readOnlyTransaction) {
    if (readOnlyTransaction) {
      return waitForUpdates(replicaDriver.createSession());
    } else {
      return masterDriver.createSession();
    }
  }
waitForUpdates will be explained shortly, the createSession call should be replaced by code that pools the sessions. Since there are two drivers you also need to set up two collections of sessions.

With the setup above you will be able to perform both readonly and update transactions, with the readonly transactions having improved performance since they use the replica. However, X-Hive/DB replication is 'lazy', which means that when an update transaction on the master federation finishes it is not guaranteed that a transaction started directly after on the replica will 'see' the changes. This can be problematic if in your application you e.g. add a document to a library, and then immediately after that show the contents of the library to the user in a readonly transaction, the library contents might not be updated on the replica yet in such a case. To overcome this, it is possible to have a session on the replica wait for updates made in a session on the master server. In the session pool code you could register a timestamp each time a readwrite session completes:

  XhiveSessionIf.TimeStamp currentWaitTimeStamp = null;

  synchronized returnSession(XhiveSessionIf session) {
    // Regular session pool code
    ....
    if (session.getDriver().equals(masterDriver)) {
      currentWaitTimeStamp = session.getUpdateTimeStamp();
    }
  }
and then from getSession call a routine that waits for the updates to be available on the replica before continuing:
  XhiveSessionIf waitForUpdates(XhiveSessionIf session) {
    if (currentWaitTimeStamp != null) {
      session.waitForTimeStamp(currentWaitTimeStamp);
      currentWaitTimeStamp = null;
    }
    return session;
  }
(Please note that in practice, 'lazy' does not mean it will take long for the changes to be replicated, only that it is not guaranteed, so in practice XhiveSessionIf.waitForTimeStamp(...) should complete immediately or quickly).