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.cs: ConfigureReplication
1public static void ConfigureReplication() 2 { 3 Db4oFactory.Configure().GenerateUUIDs(Int32.MaxValue); 4 Db4oFactory.Configure().GenerateVersionNumbers(Int32.MaxValue); 5 }

ReplicationExample.vb: ConfigureReplication
1Public Shared Sub ConfigureReplication() 2 Db4oFactory.Configure().GenerateUUIDs(Int32.MaxValue) 3 Db4oFactory.Configure().GenerateVersionNumbers(Int32.MaxValue) 4 End Sub

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

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

ReplicationExample.cs: ConfigureReplicationPilot
1public static void ConfigureReplicationPilot() 2 { 3 Db4oFactory.Configure().ObjectClass(typeof(Pilot)).GenerateUUIDs(true); 4 Db4oFactory.Configure().ObjectClass(typeof(Pilot)).GenerateVersionNumbers(true); 5 }

ReplicationExample.vb: ConfigureReplicationPilot
1Public Shared Sub ConfigureReplicationPilot() 2 Db4oFactory.Configure().ObjectClass(GetType(Pilot)).GenerateUUIDs(True) 3 Db4oFactory.Configure().ObjectClass(GetType(Pilot)).GenerateVersionNumbers(True) 4 End Sub

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.cs: Replicate
01public static void Replicate() 02 { 03 IObjectContainer desktop = Db4oFactory.OpenFile(DtFileName); 04 IObjectContainer handheld = Db4oFactory.OpenFile(HhFileName); 05 Db4objects.Drs.IReplicationSession replication = Db4objects.Drs.Replication.Begin(handheld, desktop); 06 /* 07 * There is no need to replicate all the objects each time. 08 * ObjectsChangedSinceLastReplication methods gives us 09 * a list of modified objects 10 */ 11 IObjectSet changed = replication.ProviderA().ObjectsChangedSinceLastReplication(); 12 //Iterate changed objects, replicate them 13 while (changed.HasNext()) 14 { 15 Pilot p = (Pilot)changed 16 .Next(); 17 if (p.Name.StartsWith("S")) 18 { 19 replication.Replicate(p); 20 } 21 } 22 replication.Commit(); 23 }

ReplicationExample.vb: Replicate
01Public Shared Sub Replicate() 02 Dim desktop As IObjectContainer = Db4oFactory.OpenFile(DtFileName) 03 Dim handheld As IObjectContainer = Db4oFactory.OpenFile(HhFileName) 04 Dim replic As IReplicationSession = Replication.Begin(handheld, desktop) ' 05 ' * There is no need to replicate all the objects each time. 06 ' * ObjectsChangedSinceLastReplication methods gives us 07 ' * a list of modified objects 08 ' */ 09 Dim provider As IReplicationProvider = replic.ProviderA() 10 Dim changed As IObjectSet = provider.ObjectsChangedSinceLastReplication() 11 'Iterate changed objects, replicate them 12 While changed.HasNext() 13 Dim p As Pilot = CType(changed.Next, Pilot) 14 If p.Name.StartsWith("S") Then 15 replic.Replicate(p) 16 End If 17 End While 18 replic.Commit() 19 End Sub

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.cs: ConfigureForExisting
1public static void ConfigureForExisting() 2 { 3 Db4oFactory.Configure().ObjectClass(typeof(Pilot)).EnableReplication(true); 4 Db4objects.Db4o .Defragment.Defragment.Defrag(DtFileName); 5 }

ReplicationExample.vb: ConfigureForExisting
1Public Shared Sub ConfigureForExisting() 2 Db4oFactory.Configure().ObjectClass(GetType(Pilot)).EnableReplication(True) 3 Defragment.Defragment.Defrag(DtFileName) 4 End Sub

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