1

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 ?

Jean-François Beauchamp
  • 5,485
  • 8
  • 43
  • 77

2 Answers2

1

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.

  • I installed Ground DB in the past month or two from https://atmospherejs.com/ground/db. Is this Ground DB II? – Jean-François Beauchamp Jul 29 '16 at 22:21
  • Also, does Groud DB II have provision for that middleware observer? Or would I have to fork it all? All my checkboxes on the UI are bound to an object with the checkbox state (true or false), the ID of the user who did the change, and the date of the change. So I would need to compare this data with what is it the DB before a document is updated, and have the ability to cancel the update or let it run. Could you please give me a hint on where I could tie in such code? – Jean-François Beauchamp Jul 29 '16 at 22:26
  • By the way, I appreciate getting an answer from the author of the library. :) – Jean-François Beauchamp Jul 29 '16 at 22:35
0

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);
  }
}));