4
votes

In our scenario we have a local CouchDB connected to a node.js server that generates data. The data includes users and their access to certain tables within this database.

This local CouchDB instance should be replicated to a remote CouchDB in the cloud including the user and security rules as specified in the tables _users and _security for each single database.

Replication for all the data to the remote CouchDB works but the problem is:

  • Only server and database admins can create users. Therefore, the replication task needs admin credentials. If there is no other solution I think I can live with that.

  • _security documents are unversioned and therefore cannot be replicated.

Is there a good way to keep the two databases in sync even if the local CouchDB instance is offline at the time of user creation. (user and security).

Update:

One way that I have in mind is to create a design document on the server that gets called every time a new user is created. Then, this function creates a new database for the newly created user and adds the username to the _security rules.

I would like to avoid an additional application on the server that listens to the _changes feed and acts on new updates.

2

2 Answers

0
votes

One way that I have in mind is to create a design document on the server that gets called every time a new user is created. Then, this function creates a new database for the newly created user and adds the username to the _security rules.

There is a plugin called couch per user that allows you to do just that. Install it and for every new user in the _users database it will automatically create a database for that user.

You are right that the _security documents are not versioned. But regular documents are. So if you store the _security object as a regular couchdb document you solve the problem of replicating them. Now all we need ensure is when the document containing the _security object is replicated it is automatically added to the _security of target database.

If you think of validatation functions as "before update" event handlers then they can used for this task. What we need to do is

  1. Create a validation function that looks for documents containing _security object.
  2. Once the object is found make an http request to the database's _security field and store in it the contents of the object.
  3. "Pass" object containing _security so it is saved on a local database.

Now default javascript validate functions can't make http calls so you will need to change your query server. I know that at least erlang and python query servers allow you to make http calls. But try the one in your favorite language first.

Example:-

    {
    _id:adsfasf,
    _rev:3-bd25f9790db7a8034d9415da7a4d625,
    "type":"security",
    _security: 
    {
        "admins": {
            "names": [
                "superuser"
            ],
            "roles": [
                "admins"
            ]
        },
        "members": {
            "names": [
                "user1",
                "user2"
            ],
            "roles": [
                "developers"
            ]
        }
    }

    }
}

Validation function

function(newDoc,oldDoc,userCtx,secObj){

if(newDoc.type ==="security"){
//make an http call to db/_security and post the _security field there

  }
}

You don't need to listen to the changes feed this way. SImply using a combination of normal couchdb documents and validate functions (in a different query server) you can do what you want.

Performance issues

Having a validate function can certainly slow down the insertion time since it will perform a check on each couchdb document before inserting.

Erlang validation function will be the fastest and most resource efficient among different query servers. It shouldn't be too difficult to write it in erlang since it is just an if block and a request to http. This example is untested but it should get you started.

fun({N_Doc},_Old_Doc,_User,_Sec)->
    case proplists:get_value(<<"type">>,N_Doc) of 
        <<"_serurity">>-> 
           httpc:request(
               post,
               {"http://localhost:5984/yourdb/_security", 
                [], 
                "application/json",
                ejson:encode(proplists:get_value(<<"_security">>))},
               [],[]),
             1;
    <<"some_bad_value">>->
        {[{<<"forbidden">>, "Nope this won't do"}]};
    _->
       1
    end
end.

The function above simply waits for a document of type "security" and posts it to the _security endpoint of the database. A 1 response means every thing is okay. The document has been validated and now it can be stored. A forbidden response does not allow the document to be stored. Hope this helps.

0
votes

I just released a new tool called replicate-couchdb-cluster, which provides a fault tolerant way of replicating an entire cluster of DBs. It also allows for concurrency so it can greatly speed up the time it takes to replicate a cluster.

There is also a docker image that you can use to easily set up a continuous replication.

I hope this helps!