Skip to content

Latest commit

 

History

History
188 lines (147 loc) · 6.18 KB

replication.md

File metadata and controls

188 lines (147 loc) · 6.18 KB

Replication with Cloudant Sync Android

This functionality is available in versions 0.3.0 and up.

This document discusses setting up replication with the library, along with synchronising data by using two-way (bi-direction) replication.

Setting Up For Sync

Currently, the replication process requires a remote database to exist already. To avoid exposing credentials for the remote system on each device, we recommend creating a web service to authenticate users and set up databases for client devices. This web service needs to:

  • Handle sign in/sign up for users.
  • Create a new remote database for a new user.
  • Grant access to the new database for the new device (e.g., via API keys on Cloudant or the _users database in CouchDB).
  • Return the database URL and credentials to the device.

Replication on the Device

From the device side, replication is straightforward. You can replicate from a local datastore to a remote database, from a remote database to a local datastore, or both ways to implement synchronisation.

First we create a simple listener that just sets a CountDownLatch when the replication finishes so we can wait for a replication to finish without needing to poll:

import com.google.common.eventbus.Subscribe;

/**
 * A {@code ReplicationListener} that sets a latch when it's told the
 * replication has finished.
 */
private class Listener {

    private final CountDownLatch latch;
    public ErrorInfo error = null;

    Listener(CountDownLatch latch) {
        this.latch = latch;
    }

    @Subscribe
    public void complete(ReplicationCompleted event) {
        latch.countDown();
    }

    @Subscribe
    public void error(ReplicationErrored event) {
        this.error = event.errorInfo;
        latch.countDown();
    }
}

Next we replicate a local datastore to a remote database:

import com.cloudant.sync.replication.ReplicationFactory;
import com.cloudant.sync.replication.Replicator;

// username/password can be Cloudant API keys
URI uri = new URI("https://username:[email protected]/my_database");
Datastore ds = manager.openDatastore("my_datastore");

// Create a replicator that replicates changes from the local
// datastore to the remote database.
Replicator replicator = ReplicatorFactory.oneway(ds, uri);

// Use a CountDownLatch to provide a lightweight way to wait for completion
latch = new CountDownLatch(1);
Listener listener = new Listener(latch);
replicator.getEventBus().register(listener);
replicator.start();
latch.await();
replicator.getEventBus().unregister(listener);
if (replicator.getState() != Replicator.State.COMPLETE) {
    System.out.println("Error replicating TO remote");
    System.out.println(listener.error);
}

And getting data from a remote database to a local one:

// username/password can be Cloudant API keys
URI uri = new URI("https://username:[email protected]/my_database");
Datastore ds = manager.openDatastore("my_datastore");

// Create a replictor that replicates changes from the remote
// database to the local datastore.
replicator = ReplicatorFactory.oneway(uri, ds);

// Use a CountDownLatch to provide a lightweight way to wait for completion
latch = new CountDownLatch(1);
Listener listener = new Listener(latch);
replicator.getEventBus().register(listener);
replicator.start();
latch.await();
replicator.getEventBus().unregister(listener);
if (replicator.getState() != Replicator.State.COMPLETE) {
    System.out.println("Error replicating FROM remote");
    System.out.println(listener.error);
}

And running a full sync, that is, two one way replicaitons:

// username/password can be Cloudant API keys
URI uri = new URI("https://username:[email protected]/my_database");
Datastore ds = manager.openDatastore("my_datastore");

replicator_pull = ReplicatorFactory.oneway(uri, ds);
replicator_push = ReplicatorFactory.oneway(ds, uri);

// Use a latch starting at 2 as we're waiting for two replications to finish
latch = new CountDownLatch(2);
Listener listener = new Listener(latch);

// Set the listener and start for both pull and push replications
replicator_pull.getEventBus().register(listener);
replicator_pull.start();
replicator_push.getEventBus().register(listener);
replicator_push.start();

// Wait for both replications to complete, decreasing the latch via listeners
latch.await();

// Unsubscribe the listeners
replicator_pull.getEventBus().unregister(listener);
replicator_push.getEventBus().unregister(listener);

// Unfortunately in this implementation we'll only record the last error
// the listener saw
if (replicator_pull.getState() != Replicator.State.COMPLETE) {
    System.out.println("Error replicating FROM remote");
    System.out.println(listener.error);
}
if (replicator_push.getState() != Replicator.State.COMPLETE) {
    System.out.println("Error replicating FROM remote");
    System.out.println(listener.error);
}

Using IndexManager with replication

When using IndexManager for indexing and querying data, it needs to be updated after replication completes:

import com.cloudant.sync.replication.ReplicationFactory;
import com.cloudant.sync.replication.Replicator;
import com.cloudant.sync.indexing.IndexManager;

// username/password can be Cloudant API keys
URI uri = new URI("https://username:[email protected]/my_database");
Datastore ds = manager.openDatastore("my_datastore");

// Create a replicator that replicates changes from the local
// datastore to the remote database.
Replicator replicator = ReplicatorFactory.oneway(ds, uri);

// Create a sample index on type field
IndexManager indexManager = new IndexManager(ds);
indexManager.ensureIndexed("type", "type");

// Use a CountDownLatch to provide a lightweight way to wait for completion
latch = new CountDownLatch(1);
Listener listener = new Listener(latch);
replicator.getEventBus().register(listener);
replicator.start();
latch.await();
replicator.getEventBus().unregister(listener);
if (replicator.getState() != Replicator.State.COMPLETE) {
    System.out.println("Error replicating TO remote");
    System.out.println(listener.error);
}

// Ensure all indexes are updated after replication
indexManager.updateAllIndexes();