Meteor GroundDB granularity for offline/online syncing

452 views Asked by At

Let's say that two users do changes to the same document while offline, but in different sections of the document. If user 2 goes back online after user 1, will the changes made by user 1 be lost?

In my database, each row contains a JS object, and one property of this object is an array. This array is bound to a series of check-boxes on the interface. What I would like is that if two users do changes to those check-boxes, the latest change is kept for each check-box individually, based on the time the when the change was made, not the time when the syncing occurred. Is GroundDB the appropriate tool to achieve this? Is there any mean to add an event handler in which I can add some logic that would be triggered when syncing occurs, and that would take care of the merging ?

2

There are 2 answers

3
AudioBubble On BEST ANSWER

The short answer is "yes" none of the ground db versions have conflict resolution since the logic is custom depending on the behaviour of conflict resolution eg. if you want to automate or involve the user.

The old Ground DB simply relied on Meteor's conflict resolution (latest data to the server wins) I'm guessing you can see some issues with that depending on the order of when which client comes online.

Ground db II doesn't have method resume it's more or less just a way to cache data offline. It's observing on an observable source.

I guess you could create a middleware observer for GDB II - one that checks the local data before doing the update and update the client or/and call the server to update the server data. This way you would have a way to handle conflicts.

I think to remember writing some code that supported "deletedAt"/"updatedAt" for some types of conflict handling, but again a conflict handler should be custom for the most part. (opening the door for reusable conflict handlers might be useful)

Especially knowing when data is removed can be tricky if you don't "soft" delete via something like using a "deletedAt" entity.

0
AudioBubble On

The "rc" branch is currently grounddb-caching-2016 version "2.0.0-rc.4",

I was thinking about something like: (mind it's not tested, written directly in SO)

// Create the grounded collection
foo = new Ground.Collection('test');

// Make it observe a source (it's aware of createdAt/updatedAt and
// removedAt entities)
foo.observeSource(bar.find());

bar.find() returns a cursor with a function observe our middleware should do the same. Let's create a createMiddleWare helper for it:

function createMiddleWare(source, middleware) {
  const cursor = (typeof (source||{}).observe === 'function') ? source : source.find();
  return {
    observe: function(observerHandle) {
      const sourceObserverHandle = cursor.observe({
        added: doc => {
          middleware.added.call(observerHandle, doc);
        },
        updated: (doc, oldDoc) => {
          middleware.updated.call(observerHandle, doc, oldDoc);
        },
        removed: doc => {
          middleware.removed.call(observerHandle, doc);
        },
      });
      // Return stop handle
      return sourceObserverHandle;
    }
  };
}

Usage:

foo = new Ground.Collection('test');

foo.observeSource(createMiddleware(bar.find(), {
  added: function(doc) {
    // just pass it through
    this.added(doc);
  },
  updated: function(doc, oldDoc) {
    const fooDoc = foo.findOne(doc._id);

    // Example of a simple conflict handler:
    if (fooDoc && doc.updatedAt < fooDoc.updatedAt) {
      // Seems like the foo doc is newer? lets update the server...
      // (we'll just use the regular bar, since thats the meteor
      // collection and foo is the grounded data
      bar.update(doc._id, fooDoc);
    } else {
      // pass through
      this.updated(doc, oldDoc);
    }
  },
  removed: function(doc) {
    // again just pass through for now
    this.removed(doc);
  }
}));