couchbaselabs / TouchDB-Android

CouchDB-compatible mobile database; Android version

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Replication works from time to time

somentelucas opened this issue · comments

Hi there, I'm getting some weird behavior when doing replication.
I have the following code and it works from time to time. If I run in debug mode and go line by line, it works more often (but still not always), but if I run it at once it never works. It looks like some of the process take longer and I don't know exactly how to make sure the replication will run properly.

CouchDbConnector dbConnector = dbInstance.createConnector("database", false);
dbConnector.createDatabaseIfNotExists();
ReplicationCommand pullCommand = new ReplicationCommand.Builder()
.source("https://xxxx.iriscouch.com/database")
.target("database")
.continuous(true)
.build();
ReplicationStatus status = dbInstance.replicate(pullCommand);

PS: even though when I get it to work the status.isOk() and status.isNoChanges() is always false, is that normal?

Thanks!

From what I can tell, it is normal for status.isOk() and status.isNoChanges() to always be false, which seems odd. I'm planning to dig in and find out why that is the case.

Can you post the output from logcat? If it's more than 25 lines, post it in a service like https://friendpaste.com/

commented

Hi,
I think ReplicationStatus is actually not doing anything useful right now, and note that the replicate() call is non-blocking. See here:
#72
https://groups.google.com/forum/?hl=en&fromgroups=#!topic/mobile-couchbase/epp16Afu8NQ

Nick

Indeed, that was the problem, so I put the sleep() to wait for the replication and it's working fine!
Although doing the replication as he says there doesn't work for me:

TDDatabase database = getServer().getDatabaseNamed(nameOfLocalDatabase);
database.open();
TDReplicator replicator = database.getReplicator(new URL(urlOfRemoteDatabase), false, false);
replicator.start();

So I'm doing as the wiki says...

ReplicationCommand pullCommand = new ReplicationCommand.Builder()
.source("https://xxx.iriscouch.com/xxx")
.target("localdb")
.continuous(true)
.build();
ReplicationStatus status = dbInstance.replicate(pullCommand);

I don't really get the difference between the two ways of replicating, if there is any...

Thank you!

The second way is preferred because it uses the Ektorp api, which is the "public" api for couchbase-lite. Using TDReplicator directly is the "internal" api and is discouraged (because it may change any time)

Using sleep() to wait for the replication probably won't be a very reliable approach (how do you know how long to sleep for?). Here's an alternative approach that uses callbacks:

https://github.com/couchbase/couchbase-lite-android/blob/master/Couchbase-Lite-Android-TestApp/src/com/couchbase/cblite/testapp/ektorp/tests/Replicator.java

(see the testPullWithObserver() test method)

I was using an approximate time, but it's far from being a reliable solution indeed.

I'm trying the solution you suggested, but I don't know if with TouchDB it would work as fine, maybe you could give me a hint here, I can't manage to get the replicator to add the observer. It's always returning null. I've tried both ways:

database.getReplicator(new URL("https://xxx.iriscouch.com/xxx"), false, false, null);
database.getActiveReplicators();

Heres the way I'm doing it and it works for me.

ReplicationStatus status = dbInstance.replicate(pushCommand);
CBLReplicator repl = db.getReplicator(status.getSessionId()); 
repl.addObserver(myObserver);

Still doesn't answer why your ways aren't working though. If you have the replicationstatus then this seems to be the preferred way to get the replicator.
Looking at the test, this is the way it is achieved as well.

@somentelucas Oh I think I know the problem.. the ability to add an observer to a replication was added after the repo was migrated from TouchDB -> Couchbase Lite. There are instructions to switch over to Couchbase Lite here.

I know it's a pain, sorry about the inconvenience.

Ah. If you don't want to move to couchbaselite at the moment you can extend EktorpAsyncClass and perform replications with that.

Exactly, it took me a while to realize it had been changed, although it was no problem at all to switch at all. Now everything os working fine, thank you!

Lucas

@tleyden I found out that the Observer wouldn't work for me for the following reason: I want the elements (TextView) to be updated as soon as the replication finishes, and they wouldn't be updated before the sleep() (before deleting the observer) finished, even thought the Observer had already been notified. And this sleep() apparently has to be long enough, because we never know how long the replication will take (once my database will grow over time).

Once the sleep() won't be avoided anyway, I found this option better

while (waitUntilDone) {
            if (repl.isRunning()) {
                Log.d(TAG, "Replicator still running");
                try {
                    Thread.sleep(1 * 1000);
                } catch (Exception e) {
                    Log.e(TAG, "Sleep exception");
                }
            } else {
                Log.d(TAG, "Replicator stopped");
                waitUntilDone = false;
            }
        }

Because I can set the sleep() very short and the widgets will be updated right after it finishes.

Would the following work for you?

       class MyObserver implements Observer {
            public boolean replicationFinished = false;

            @Override
            public void update(Observable observable, Object data) {
                CBLReplicator replicator = (CBLReplicator) observable;
                replicationFinished = !replicator.isRunning();      
                if (replicationFinished) {
                                // Add code here to update Text widget
                                replicator.deleteObserver(this);
                        }
            }
        }

        HttpClient httpClient = new CBLiteHttpClient(server);
        CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient);

        // push this database to the test replication server
        ReplicationCommand pushCommand = new ReplicationCommand.Builder()
            .source(getReplicationURL().toExternalForm())
            .target(DEFAULT_TEST_DB)
            .continuous(false)
            .build();

        ReplicationStatus status = dbInstance.replicate(pushCommand);        
        CBLReplicator repl = database.getReplicator(status.getSessionId());                 
        MyObserver myObserver = new MyObserver();       
        repl.addObserver(myObserver);

Points to note:

  1. No sleep() needed anywhere.
  2. The observer is removed only after the replication has finished.

It worked, thank you so much!