Couchbase-lite-core | program hangs after calling c4dbobs_getChanges

Hi,

I’m trying to observe database changes. I have a small program that opens/closes a database, writes and delete documents and my next challenge is to detect changes on the database on two different instances of the application.

However I am facing an issue, when I call c4dbobs_getChanges, the program hangs and I have to kill it. Code and output of the program is below.

void IC4Database::open()
{
    C4Error error;

    if (_c4Database)
        return;

    C4DatabaseConfig defaultC4DbConfig =  { kC4DB_SharedKeys | kC4DB_Create, kC4SQLiteStorageEngine, kC4RevisionTrees, { kC4EncryptionNone, {0}}};

    _c4Database = c4db_open(QSlString("/tmp/database"), &defaultC4DbConfig, &error);

    if (_c4Database == nullptr)
    {
        logC4Error("couldn't open database", error);
    }

    _observer = c4dbobs_create(_c4Database, this->databaseObserverCallback, this);

    if (_observer==nullptr)
    {
        qDebug() << "Unable to create database observer.";
    }
}

void IC4Database::databaseObserverCallback(C4DatabaseObserver *observer, void *context)
{
    qDebug() << "Observer: database changed";

    bool external;
    C4DatabaseChange changes[MAX_OUTCHANGES_BUFFER];

    qDebug() << "before call";
    int nDocs = c4dbobs_getChanges(observer, changes, MAX_OUTCHANGES_BUFFER, &external);
    qDebug() << "after call" << nDocs;
}   

Output

12:20:14.946876| [DB]: {Shared#1}==&gt; litecore::DataFile::Shared 0x7f91ab538a10 @0x7f91ab538a10
12:20:14.947045| [DB]: {Shared#1} instantiated on /private/tmp/database/db.sqlite3
12:20:14.947093| [DB]: {DB#2}==&gt; litecore::SQLiteDataFile /tmp/database/db.sqlite3 @0x7f91ab534690
12:20:14.947120| [DB]: {DB#2} Opening database
QString IC4Database::getAllDocuments(const QString &amp;, int)
Row count: 0
IC4Document *IC4Database::createDocument()
Creating document with id &quot;0\u0001&quot;
Observer: database changed
before call

Heh, I actually discovered this deadlock a few weeks ago when writing some new code that uses LiteCore directly.

The short answer: don’t call c4dbobs_getChanges inside the observer callback. Instead, have the callback schedule a later call to a function that will handle the changes (and coalesce the calls; i.e. ensure that multiple calls to the callback only trigger one call to that function.) If you can’t do that, at least have the call to c4dbobs_getChanges occur on another thread.

This “schedule a later call” is platform-specific of course … on iOS we use dispatch_async. IIRC you’re using Qt, and again IIRC it has its own event-loop API(?)

Hey Jens. Thanks for your swift reply. Problem solved. I’m queueing the c4dbobs_getChanges and it is not deadlocking any more! :slight_smile:

But I still haven’t reached my goal yet…

When I open two instances of the same program, I was expecting to have the second instance, being notified of database changes caused by the first instance but there isn’t any observer callback being called. Is this supposed?

No, we don’t support cross-process notifications. Or rather, LiteCore does not detect when changes are made to the database externally.

Hum… ok! But let’s say that we have a replicator connected to the database. If the server changes, all the connected replicators will receive the changes, right? Let’s now suppose that we have two instances of the app, each one has a database open, with a replicator connect, will it work this way? Thanks Jens!

Cross process is not a supported case. Replicators rely on pretty much the same logic as you are using so I wouldn’t think that would work either.

My application is a plugin that can be hosted several times in a certain software host. On this plugin I want the user to have a cloud bank where he store his documents. If I open another instance of the plugin I want the user to see the local documents.
Does this mean that Couchbase does not allow me to have several instances to share the same local database and replicate that database with a cloud/server database? If possible, what should be the right architecture? If not, I might need to find other solution. Please advise. Thanks.

You can share the same local database (though that is not a scenario we test) it’s just that notifications will not work between processes (the library has no way of knowing about other processes that also happen to be running another copy of it to even try to notify them and even if it did cross process messaging is a whole separate field of expertise).

My recommendation would be to try to send the message between your applications / plugins. When one of them receives a notification it can broadcast it to the others.

My application is a plugin that can be hosted several times in a certain software host.

That should be fine, since it’s in the same process. Database instances in the same process will notify each other.

(I’m assuming that instances of the plugin will share the same globals, which is usually true for shared libraries. If they don’t, the above won’t work.)

I feel hope again! :heart: