db4o Replication System (dRS)

db4o Replication System (dRS) provides functionality to periodically synchronize databases that work disconnected from each other, such as remote autonomous servers or handheld devices synchronizing with central servers.

In order to use replication, the following configuration settings have to be called before a database file is created or opened:

ReplicationExample.java: configureReplication
1public static void configureReplication(){ 2 Db4o.configure().generateUUIDs(Integer.MAX_VALUE); 3 Db4o.configure().generateVersionNumbers(Integer.MAX_VALUE); 4 }

(See the section below on how to enable replication for existing databases)

Both settings can also be configured on a per-class basis:

ReplicationExample.java: configureReplicationPilot
1public static void configureReplicationPilot(){ 2 Db4o.configure().objectClass(Pilot.class).generateUUIDs(true); 3 Db4o.configure().objectClass(Pilot.class).generateVersionNumbers(true); 4 }

Suppose we have opened two ObjectContainers from two different databases called "handheld" and "desktop", that we want to replicate. This is how we do it:

ReplicationExample.java: replicate
01public static void replicate(){ 02 ObjectContainer desktop=Db4o.openFile(DTFILENAME); 03 ObjectContainer handheld=Db4o.openFile(HHFILENAME); 04 // Setup a replication session 05 ReplicationSession replication = Replication.begin(handheld, desktop); 06 07 /* 08 * There is no need to replicate all the objects each time. 09 * objectsChangedSinceLastReplication methods gives us 10 * a list of modified objects 11 */ 12 ObjectSet changed = replication.providerA().objectsChangedSinceLastReplication(); 13 14 // Iterate changed objects, check if the name starts with "S" and replicate only those items 15 while (changed.hasNext()) { 16 replication.replicate(changed.next()); 17 } 18 19 replication.commit(); 20 }

That's all there is to it.

We are using a query that will return all objects but we could use any query we like to constrain the objects we want.

Calling whereModified() will add a constraint to the query so that it only returns the objects that have actually been modified since the last replication between both the containers in question.

After replication commit, all modified objects (INCLUDING THE ONES THAT WERE NOT REPLICATED) are considered to be "in sync" and will not show up in future "where modified" queries, unless they are modified again.

Under the Hood

Let's take a look at the necessary configuration calls to tell db4o to generate version numbers and UUIDs:<

  1. An object's version number indicates the last time an object was modified. It is the database version at the moment of the modification. The database version starts at zero and is incremented every time a transaction is commited.
  2. UUIDs are object IDs that are unique across all databases created with db4o. That is achieved by having the database's creation timestamp as part of its objects' UUIDs. Manually copying db4o database files can produce duplicate UUIDs, of course.

When the replication process is commited, the lowest database version number among both databases is set to be equal to the highest. After replication commit, therefore, both databases have the same version number and are "in sync".

Synchronizing Existing Database Files

As we learned in the last sections, Db4o.configure().generateUUIDs() and Db4o.configure().generateVersionNumbers() (or its objectClass counterparts) must be called before storing any objects to a data file because db4o replication needs object versions and UUIDs to work. This implies that objects in existing data files stored without the correct settings can't be replicated.

Fortunately enabling replication for existing data files is a very simple process:

We just need to use the Defragment tool in com.db4o.tools (distributed as a source code for the versions before 6.0) after enabling replication:

ReplicationExample.java: configureForExisting
1public static void configureForExisting(){ 2 Db4o.configure().objectClass(Pilot.class).enableReplication(true); 3 try { 4 com.db4o.defragment.Defragment.defrag("sample.yap"); 5 } catch (IOException ex){ 6 System.out .println(ex.toString()); 7 } 8 }

After a successful defragmentation our data files are ready for replication.