Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

per user db (pouchdb/couchdb) & shared data - doable?

I have the following use case / application:

a TODO app where users can: CRUD their on TODOs (I am using pouchdb/couchdb syncing). Pretty much based on Josh Morony's tutorial

Now I want to add ability of users to "share" (post only, there is no "edit"/put) their TODO items with other users, who would be able to just view (read) those (no write access etc).

I was thinking about adding a separate DB (let's call it "shared TODOs DB") where my server can write and all users can only read. So any user could potentially do .find() across that read only db, while posting in there will still be governed by a server upon requests to share their TODO coming from users.

Is there a known pattern (approach) for this? Are there any real apps / examples that already do that?

like image 783
Sergey Rudenko Avatar asked Sep 21 '17 19:09

Sergey Rudenko


People also ask

Why would you use PouchDB along with CouchDB?

PouchDB enables applications to store data locally while offline, then synchronize it with CouchDB and compatible servers when the application is back online, keeping the user's data in sync no matter where they next login.

Who uses PouchDB?

Newt lets you read, listen and write thousands of books across devices natively. It runs on Windows, Linux, macOS, Android, iOS & web. Its backed is powered by CouchDB and it uses PouchDB on the web client, desktop client and mobile native app for offline access, live sync, revisions and much more.

Is CouchDB still relevant?

Currently, we use CouchDB in various projects throughout our Big Data Insight Product Division for development and production environment setup. Mainly it is being used to store serialized (JSON formatted) unstructured data.


2 Answers

CouchDB does not offer a good way to do this, but if your backend has a facility for maintaining lots of shared databases (in addition to single-user ones) I think you could pull it off.

The initial impulse is to use continuous filtered replication to/from a central "master" hub, but this leaves you with two problems:

  • Replication can't delete documents unless they are deleted! I.e. filtered replication doesn't determine which documents exist in the target, but rather whether a document's changes will propagate to the target. The effect of this is that you can share, but you can't unshare.
  • This sort of replication doesn't scale. For N user databases you need to keep in sync, every change to the central database forces all N replications to process that change independently. Now consider that the amount of changes M that happen on the central database will be almost directly proportional to N (i.e. the more users you have the more frequently all those users will have to process changes to the central database!) You could somewhat mitigate this by adding more layers (fanning out from one central hub to semi-shared hub to individual databases), but that also adds practical complications.

How is your sharing organized? What you might be able to do is set up a shared database for each "share" combination.

When user A wants to share document D with user B, "move" the document into a new database AB. Practically this means: copy the contents of D to a new document D' in database AB, and then delete the original D from database A.

How does this sync?

Keep in mind PouchDB clients can replicate to/from more than one source database, so user A will replicate from A and AB, while user B replicates from B and AB. The trick is to use filtered replication in the opposite direction, back to the server databases:

The now-shared document D' should never go to database A or database B, and likewise an unshared document X should never go to database AB. Something like this:

┌───────────────────────┐                                  
│  server:5984/user_a   │                                  
└───┬───────────────▲───┘                                  
    └─┐           ┌─┘    ┌──────────────────────────────┐  
      │           ●──────│ if (doc.shared_with == null) │  
    ┌─▼───────────┴─┐    └──────────────────────────────┘  
    │     local     │                                      
    └──▲──────────┬─┘                                      
     ┌─┘          └─┐      ┌──────────────────────────────┐
     │              ●──────│ if (doc.shared_with == 'ab') │
 ┌───┴──────────────▼────┐ └──────────────────────────────┘
 │ server:5984/shares_ab │                                 
 └───────────────────────┘              

So, assuming the share database(s) are already set up, when the local client wants to share some data it actually adds _deleted:true to the original (unshared) document and creates a new (shared) document. The deleted original propagates to the user_a database on the server and the created copy propagates to the shares_ab.

Unsharing then works pretty much the same: the client adds _deleted:true to the shared document and recreates the data again in a new unshared copy. From B's perspective, the document had showed up once it was in shares_ab and now disappears because it is deleted from shares_ab.

(Instead of user "pairs" you could extend this to ad-hoc sets of users, or simplify it to specific groups that users are already in, or whatnot. The key idea is to create an actually shared database for each unique sharing context needed.)

like image 189
natevw Avatar answered Nov 15 '22 19:11

natevw


So for this kind of project, per-user database is the recommended pattern for your databases.

Idea

So basically, you will have :

  • One private database per user (with write/read permissions)
  • One central database read-only

As for the central database, you need it to be read-only and you also need to allow shared documents only. You need some kind of application proxy for this. You can basically build an API on top of the central database and allow access to shared documents only.

Then, you can setup replications from each user database to the central database and persist de replication in the _replicator database.

couchperuser

I'm not sure that per-user database plugin is working at the moment with the version 2.X.X but you can do it yourself with some sort of application process (Create the user, then create the database, then manage permissions of the new database)

like image 28
Alexis Côté Avatar answered Nov 15 '22 19:11

Alexis Côté