Fetch document created from the CBServer Administration

Hi guys!

Trying to get the hang of this, and ran into a question here at the start.

  1. I have CBServer 6.5 set up in the cloud
  2. I have CBGateway in the cloud
  3. I’m making a Xamarin.Forms app (dot-net c#). This class works fine. (abbreviated to show the pertinent areas)

============================================================

class DB : IDisposable
{
    Database _database;
    Replicator _replicator;

    public DB()
    {
        var defaultDirectory = Service.GetInstance<IDefaultDirectoryResolver>().DefaultDirectory() ;
        var databaseConfig = new DatabaseConfiguration { Directory = defaultDirectory } ;
        _database = new Database("profile", databaseConfig) ;
        Uri _remoteSyncUrl = new Uri("ws://xxx.xxx.xxx.xxx:4984");
        var targetUrlEndpoint = new URLEndpoint(new Uri(_remoteSyncUrl, "profile"));
        var configuration = new ReplicatorConfiguration(_database, targetUrlEndpoint)
        {
           ReplicatorType = ReplicatorType.PushAndPull,
           Continuous = true
        };
        _replicator = new Replicator(configuration);
        _replicator.Start();
    }

    public string MakeTestDoc()
    {
        using (var mutableDoc = new MutableDocument("test1"))
        {
           mutableDoc.SetString("foo", "bar");
           _database.Save(mutableDoc);
           return mutableDoc.Id;
        }
    }

    public string GetTestDoc(string TestID)
    {
        var doc = _database.GetDocument(TestID);
        if (doc == null)
            return "null" ;
        return doc.GetString("foo");
     }
}

============================================================

And here’s how I’m using it:

============================================================

DB a = new DB() ;
a.MakeTestDoc();
Console.WriteLine(a.GetTestDoc("test1")) ;

============================================================

GetTestDoc() returns “bar” like it should. And when I go to CBServer Administration and look in the bucket, the document is there, and looks exactly like we would expect it to look.

Here’s where my question comes up…

Next, I go to the the CBServer Administration, click into my bucket and use the “Add Document” button to create an identical document with “test2” as its Name & DocumentID. And then I run:

============================================================

Console.WriteLine(a.GetTestDoc("test1")) ;
Console.WriteLine(a.GetTestDoc("test2")) ;

============================================================

The first line works fine, same as before. The second line returns “null”.

Why can’t it fetch the document that was created via CBServer Administration?

Here’s the metadata for the 2 documents, they’re quite similar. No channels.

{
  "meta": {
    "id": "test1,
    "rev": "1-161e085e3e2f00000000000000000000",
    "expiration": 0,
    "flags": 0,
    "type": "json"
  },
  "xattrs": {
    "_sync": {
      "rev": "1-9080e71ec785faccac6152e6cb5611cc7748de82",
      "sequence": 74,
      "recent_sequences": [
        74
      ],
      "history": {
        "revs": [
          "1-9080e71ec785faccac6152e6cb5611cc7748de82"
        ],
        "parents": [
          -1
        ],
        "channels": [
          null
        ]
      },
      "cas": "0x00002f3e5e081e16",
      "value_crc32c": "0x5451e331",
      "time_saved": "2020-07-02T20:08:37.146443258Z"
    }
  }
}


{
  "meta": {
    "id": "test2",
    "rev": "2-161e0a6449cf00000000000002000006",
    "expiration": 0,
    "flags": 33554438,
    "type": "json"
  },
  "xattrs": {
    "_sync": {
      "rev": "1-a9be417c554029c89a25d15f79877101",
      "sequence": 77,
      "recent_sequences": [
        77
      ],
      "history": {
        "revs": [
          "1-a9be417c554029c89a25d15f79877101"
        ],
        "parents": [
          -1
        ],
        "channels": [
          null
        ]
      },
      "cas": "0x0000cf49640a1e16",
      "value_crc32c": "0xf1dd538a",
      "time_saved": "2020-07-02T20:45:42.122088852Z"
    }
  }
}

Thanks for looking at this. Have a great day!

Kind regards,
David

This behavior is not allowed by default, you will have to make sure that your Sync Gateway is set up correctly to read changes made directly to the bucket.

Thanks for the quick reply! As far as I know, my sync_gateway should be allowing that. I have import_docs and enable_shared_bucket_access turned on. Here’s my sync_gateway config json:

{
    "adminInterface":":4985",
    "interface":":4984",
    "logging": {
       "console": {
            "log_keys": ["*"]
        }
    },
   "databases": {
       "profile": {
           "num_index_replicas":0,
           "users": { "GUEST": { "disabled": false } },
           "bucket":"prodigy",
           "enable_shared_bucket_access": true, 
           "import_docs": true,
           "password": "xxxxx,
           "server": "http://cb65-server-central1-a-1:8091",
           "username": "sync_gateway",
           "sync": `function (doc, oldDoc) {
                if (doc.ID) {
                    channel(doc.ID);
                }
            }`
        }
    }
}

In that case it’s out of my area of expertise. Someone from the Sync Gateway team might know ( @bbrks ? )

Solved!

There were two things standing in my way. Here’s how I conquered them.

  1. First obstacle

Because the documents weren’t in any channel, my replicator didn’t pull them.

I noticed this when I deleted my local CBLite database, created a new (blank) one, and then failed to fetch the test1 document. It was still in the CBServer bucket, but the device that created it wasn’t able to replicate it. Thus, the reason I could fetch it before is because it persisted locally after being created… I was never pulling down any documents with my replicator, I was only pushing them up to the CBServer bucket. (Which gave me a false sense that everything was connected properly.)

Right now I’m just using GUEST access (not login/password) so the quick-and-dirty solution was to put all my documents in the public channel (!). This is done with a sync_function like so:

"sync": `function (doc, oldDoc) {
        channel('!');
     }
 }`

When import_docs and enable_shared_bucket_access are turned on, sync_gateway does apply this sync_function to documents created directly in the bucket from the CBServer Administration website like I was doing above.

  1. Second obstacle

In my C# app, I needed to wait for the initial replication to finish before trying to fetch the document. To do that, I created a private boolean variable for my DB class called _InitialReplicationComplete and inserted it into my constructor like so:

    _replicator = new Replicator(configuration);
    _replicatorListenerToken = _replicator.AddChangeListener(OnReplicatorUpdate);
    _InitialReplicationComplete = false;  //  <<<<<<<<<<<<<<<<<< NEW 
    _replicator.Start();
    WaitForInitialReplication();  //  <<<<<<<<<<<<<<<<<< NEW 

My change listener was updated to report when initial replication finished:

void OnReplicatorUpdate(object sender, ReplicatorStatusChangedEventArgs e)
{
   var status = e.Status;

    switch (status.Activity)
    {
        case ReplicatorActivityLevel.Busy:
             Console.WriteLine("Busy transferring data.");
            break;
        case ReplicatorActivityLevel.Connecting:
            Console.WriteLine("Connecting to Sync Gateway.");
            break;
        case ReplicatorActivityLevel.Idle:
            Console.WriteLine("Replicator in idle state.");
            _InitialReplicationComplete = true;  //  <<<<<<<<<<<<<<<<<< NEW 
            break;
        case ReplicatorActivityLevel.Offline:
            Console.WriteLine("Replicator in offline state.");
            _InitialReplicationComplete = true;  //  <<<<<<<<<<<<<<<<<< NEW 
            break;
        case ReplicatorActivityLevel.Stopped:
            Console.WriteLine("Completed syncing documents.");
            _InitialReplicationComplete = true;  //  <<<<<<<<<<<<<<<<<< NEW 
            break;
    }

    if (status.Progress.Completed == status.Progress.Total)
    {
        Console.WriteLine("All documents synced.");
    }
    else
    {
        Console.WriteLine($"Documents {status.Progress.Total - status.Progress.Completed} still pending sync");
    }
}

And then a simple synchronous wait function was created:

public void WaitForInitialReplication()
{
    while(_InitialReplicationComplete == false) { ; }
}

Thanks @borrrden for pointing me in the right direction!

Have a great weekend!

Kind regards,
David