Public channel in Sync Gateway


#1

I have a scenario that I have trouble implementing.

The data I’m using in my app is split into two categories:

  1. Meta data (public to everybody - and some of it needed for registration of a new user)
  2. User data (specific to the user logged in)

Ideally, I would like to start sync. without logging in (i.e. using the guest account). And then when the user has authenticated I’ll log in and and sync user specifically for the user.

However, once I have enabled the guest account and started sync without logging in then I cannot get it to sync. the user channel when logging in. It logs in successfully (i.e. I don’t get an error) - but it just sync’s the same public channel and ignores the user channel.

How can I obtain this functionality?


#2

Try some thing like this.

I would also separate data on the device into two different DB too.


#3

Thanks for your reply @househippo

I already have the data in one bucket on the server so I’m not sure your suggestion is an option - not easily anyway :slight_smile:

Just to understand your suggestion - you want to split the data into two buckets because then you can enable the guest account on one and not the other?

Is there another way I can “force” the sync. gateway (or rather replicator in the app) to re-evaluate the login and not use the guest account when the user logged in? E.g. by somehow specifying a channel that guest does not have access to?

Could I access data (in the “!” channel) without being logged in (and the guest account not enabled) - so when the user is logged in the user specific channels will also be sync’ed.


#4

Correct.

You have to auth to get data. either a specific user or guest.

NOTE - I’m not a big fan of using the “guest” user as general shared user , b/c in the real world you will not be able to debug sync issues if everybody comes in as the same user.


#5

Ok, thanks for your response. And I fully agree on the disadvantages of using a “generic” user.

However, I need to be able to sync. some data prior to the user being registered - and may even need to give them access to some of the data and functionality in the app without being registered.

Would it work if I created a separate “anonymous” user that I use until (hopefully) the user registers to get the full functionality - and then I could stop the replicator and start a new one with the user credentials as soon as I have created the user in the sync gateway (via the REST API)? It would still be a generic user - but seen from sync, gateway it would be a “real” user - with access to the “!” channel only.


#6

Hi John,

Your suggestion sounds good to me - by setting up a specific “anonymous” user as you suggest, your application would be able to choose between replicating with the anonymous user or with a “proper” user where available.

An advantage of this approach is that its more future-proof - right now you want all anonymous users to see the same data, but in the future you might what to differentiate different groups of anonymous/unregistered users. Using this approach would make that easy, whereas using the built-in ‘guest’ user would complicate the transition.

Ta,
Jim


#7

Thanks for verifying, Jim.

I’ll have a go with that concept :+1:


#8

Hi Jim

I’m testing the concept in a small app where I set the user manually (switching between “anonymous” and “the real user”).

When I use the anonymous user I seem to get all the “public” data. But when I try my real user it doesn’t seem to work. I don’t get the extra data that I expect - so I must be doing something wrong.

This is my current sync formula:

function (doc, oldDoc) {
  // Document type is mandatory
  if (!doc.type){
    throw({forbidden: "Document type is required."});
  }
  if ((doc.type == 'EnvLake' || doc.type == 'EnvMeasurement' || doc.type == 'Feedback' || doc.type == 'ActivityLog') && oldDoc){
    throw({forbidden: "Document type not allowed to sync to mobile..."});
  }
  // All public docs are available in the app
  if (doc.ispublic) {
    channel('!');
  }
  // All non-club fishing trips and catches are available (for stats)
  if ((doc.type == 'FishingTrip' || doc.type == 'Catch') && doc.clubonlykey == undefined) {
    channel('!');
  }
  // All non-specific user info is available (for stats)
  if (doc.type == 'User') {
    channel('!');
  }
  // Only docs "owned" by user can be updated
  var key;
  if(doc.type == 'User'){
    key = doc.key;
  } else if(doc.type == 'FishingTrip' || doc.type == 'Catch' || doc.type == 'Photo' || doc.type == 'Private'){
    key = doc.userkey;
  }
  if(key){
    requireUser(key);
    channel('channel.' + key);
    access(key,'channel.' + key);
  }
}

If I check the users in the sync.gateway then they look Ok:

{
  "name": "anonymous",
  "all_channels": [
    "!"
  ]
}

and

{
  "name": "587CE5200641ABD9C1257E500051DDCD",
  "all_channels": [
    "!",
    "channel.587CE5200641ABD9C1257E500051DDCD"
  ]
}

However, on the client side I see no channels after having started the replicator with the real user…?

If I look at the channels in the Sync Gateway then they seem all right. One thing that I’m uncertain about though - with the above formula then the “public” documents for the “real user” will be in both the “!” channel and the user’s “own” channel (for read AND write). Is that Ok - or is this something that I should avoid to not “confuse” the Sync. Gateway?

This is a simplified version of what I do in my app:

                Database database = new Database("fangst");
               // Init Replication
                var targetEndpoint = new URLEndpoint(new Uri("ws://xxx.dalsgaard-data.dk:4984/data"));
                var replConfig = new ReplicatorConfiguration(database, targetEndpoint);
                // Add authentication
                string USER = "587CE5200641ABD9C1257E500051DDCD";
                string PASSWORD = "xxxxxxxxx";
                //    USER = "anonymous";
                //    PASSWORD = "yyyyyyyy";
                Console.WriteLine("Log in with user: " + USER);
                replConfig.Authenticator = new BasicAuthenticator(USER, PASSWORD);
                replConfig.Continuous = true;
                replConfig.ReplicatorType = ReplicatorType.PushAndPull;

                // Create replicator
                Replicator replicator = new Replicator(replConfig);
                replicator.AddChangeListener(OnReplicationEvent);
                replicator.ResetCheckpoint();
                try
                {
                    Console.WriteLine("Try to start the replicator...");
                    replicator.Start();
                    Console.WriteLine("repl. channels: " + replicator.Config.Channels?.ToString());
                }
                catch (Exception ex)
                {
                    Console.WriteLine("ERROR: " + ex.ToString());
                }

Have I (still) misunderstood how the sync. works?


#9

@jim.whitson - any ideas?


#10

Hi John,

That’s interesting - let me check with a colleague, I’m not 100% on the code-level details of synchronisation.

Ta,
Jim


#11

Quick question: What do you get if you hit the _active_tasks API endpoint (1) while your test client is running?


#12

Hmmmm… I must be doing that wrong as I only get: []

I call this url via GET: http://xxx.dalsgaard-data.dk:4985/_active_tasks - was that what you meant?

I tried with both users…


#13

Yeah… I don’t really understand this. Now I deleted the local database to make sure that I forced a new replication of everything - and still that endpoint just showed: [] - so I must be doing it incorrectly…

I user Postman to send the request - and that works fine for other requests (like getting info about the bucket)


#14

Yeah, so that suggests that the replication isn’t even starting on the client side - do you have debug-level logging in CB Lite?


#15

Well, it does start… anyway, it does if I delete the database and re-run the test app :slight_smile:

I’m not sure what you mean by “debug-level lopgging in CB Lite” - is that a flag ? I tried to locate it but couldn’t find it on the Db or Repl. configs?

I can see that the replication is starting. This is the output from the app (my “prints” and some thread info that is only showing when it is replicating:

2018-10-10 10:34:28.778665+0200 cb2.iOS[87618:14131135] Instantiate cb2.Services.DbDataStore
10:34:28.856492| [Couchbase]: (Startup) [1] CouchbaseLite/2.1.0 (.NET; iOS 12.0.0; iPhone) Build/232 LiteCore/2.1.0 (1275) Commit/26b467a
2018-10-10 10:34:28.865247+0200 cb2.iOS[87618:14131135] Database 'fangst' created
2018-10-10 10:34:28.876286+0200 cb2.iOS[87618:14131135] Log in with user: anonymous
2018-10-10 10:34:28.880699+0200 cb2.iOS[87618:14131135] Try to start the replicator...
Thread started:  #2
Thread started:  #3
2018-10-10 10:34:28.907423+0200 cb2.iOS[87618:14131135] repl. channels:
Thread started:  #4
2018-10-10 10:34:28.912058+0200 cb2.iOS[87618:14131135] New database: -
2018-10-10 10:34:28.913923+0200 cb2.iOS[87618:14132322] Replication: Connecting to Sync.Gateway
2018-10-10 10:34:28.914387+0200 cb2.iOS[87618:14132322] Replication status: Couchbase.Lite.Sync.ReplicatorProgress - activity: Connecting. Completed: 0 of 0
Thread finished:  #3
Thread started:  #5
Thread started: <Thread Pool> #6
Thread started: <Thread Pool> #7
Thread started: <Thread Pool> #8
Thread started: <Thread Pool> #9
Thread started: <Thread Pool> #10
Thread finished:  #4
Thread started: <Thread Pool> #11
Thread started: <Thread Pool> #12
Thread started: <Thread Pool> #13
Thread started:  #14
Thread finished:  #14
Thread started:  #15
Thread finished:  #15
Thread started:  #16
Thread finished:  #16
Thread started:  #17
Thread started:  #18
2018-10-10 10:34:29.018868+0200 cb2.iOS[87618:14132340] Replication: Busy transferring data
2018-10-10 10:34:29.019048+0200 cb2.iOS[87618:14132340] Replication status: Couchbase.Lite.Sync.ReplicatorProgress - activity: Busy. Completed: 0 of 0
Thread finished:  #18
Thread started:  #19
Thread started: <Thread Pool> #20
Thread started: <Thread Pool> #21

#16

Check out the docs

Did you inspect the document that you expect to be synced over to the user via the user channel and confirm that the channel assignment is expected ? You can use the _raw document API on Sync Gateway.


#17

Hi Priya

Thanks for your pointer to the documentation. I tried to find an example of how to implement it - but the sample code is “invisible” in e.g. this blog post: https://blog.couchbase.com/couchbase-net-sdk-20-development-series-logging/

… I’ll search for some other samples - but thought someone ought to change the blog post :slight_smile:

When I look at the document there are a couple of surprises (to me anyway) as there is more than one “channel” with the same channel name:

{"_sync":{"rev":"6-0b7621a0ad1f948b0803b92352b75799","sequence":222240,"recent_sequences":[222004,222196,222206,222224,222232,222240],
"history":{"revs":["4-6415f63cb71a6464d33c5613db88572e","5-8087c9ccea55c55c3c663e73a77bcf9d","6-0b7621a0ad1f948b0803b92352b75799","3-29c6f78c15d85f67f2825b9b1026c93f","1-4a4eb568127cd010a7ad213e68468771","2-a297d0bb8917af2a0dd2e305e166af32"],
"parents":[3,0,1,5,-1,4],
"channels":[["channel.587CE5200641ABD9C1257E500051DDCD"],["channel.587CE5200641ABD9C1257E500051DDCD"],
["channel.587CE5200641ABD9C1257E500051DDCD"],["channel.587CE5200641ABD9C1257E500051DDCD"],
["channel.587CE5200641ABD9C1257E500051DDCD"],["channel.587CE5200641ABD9C1257E500051DDCD"]]},
"channels":{"channel.587CE5200641ABD9C1257E500051DDCD":null},"access":{"587CE5200641ABD9C1257E500051DDCD":{"channel.587CE5200641ABD9C1257E500051DDCD":222004}},
"cas":"0x00007414355a5315","value_crc32c":"","time_saved":"2018-09-11T15:04:41.886150329+02:00"},
"address":"Solbjergvej 42\r\nSolbjerg","cellphone":"40566308","city":"Høng","country":"DK","email":"john@dalsgaard-data.dk",
"name":"John Dalsgaard","phone":"49141248","sex":"M",
"type":"Private","userkey":"587CE5200641ABD9C1257E500051DDCD","zip":"4270"}

I have added some linebreaks to the raw format for easier reading

With the setup of the sync. formula and channels applied to the users above - does this document look “right” or is the problem in here…?


#18

Ok, found the examples elsewhere in the documentation. Thanks! For those reading this later then you need to use something like this:

Database.SetLogLevel(LogDomain.Replicator, LogLevel.Debug);

What is really interesting (and to some extend frustrating!) is that when I ran the same code now with logging then it seemed to work when switching between the users… The only thing that has happened is that time has passed :slight_smile:

I did try to restart the entire CB Server (including the SG) - and that still didn’t work a couple of days ago…

Well, anyway, I seem to be able to continue with testing (and learning!) so thank you for chimming in @priya.rajagopal and @jim.whitson :pray:

There is still one issue though - I cannot see any active sessions using the REST API - what could be the reason for that?


#19

Never mind about the .../_active_tasks API endpoint. It seems it cannot show active sessions with Couchbase Lite (according to this issue). However, it would be a really nice metric to be able to follow on the Sync. Gateway :wink:


#20

Glad your issue was resolved but want to clarify that the blog you are referring to is the server side Couchbase .Net SDK. Thats the SDK you would use on your server backend apps to talk to Couchbase server.
It is NOT referring to Couchbase Lite .Net database framework. As you have already figured out, Couchbase Lite documentation is here