Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When processing an update of same value for multiple fields in a document, update it in single operation #54

Open
mitar opened this issue Sep 3, 2018 · 0 comments

Comments

@mitar
Copy link
Member

mitar commented Sep 3, 2018

For example, if a document TestDocument has multiple referencing User collection, and a particular TestDocument document is referencing one document in User, when the latter is removed, we should make only single operation to update all those fields in the TestDocument document.

Currently we do multiple operations, which means that further observe callbacks are triggered but now the document referenced in fields not yet updated does not exist, so an error is shown.

It seems not not all operations can be yet be made so (see https://jira.mongodb.org/browse/SERVER-6566), but ones wher we loop and update each element in an array could since 3.5.2 (see https://jira.mongodb.org/browse/SERVER-1243).

Meteor 1.7 updated MongoDB to 3.6.4 so for those versions we could update our looping updates over array elements to do that in one operation.

If we could do also other operations all together then we should probably at every change do one update to update all fields at once. Current logic setups multiple observes for each field and is updating one by one.

A test for the example above is:

import {Meteor} from 'meteor/meteor';
import {Document} from 'meteor/peerlibrary:peerdb';

import Future from 'fibers/future';

class User extends Document {}

User.Meta({
  name: 'User',
});

class TestDocument extends Document {}

TestDocument.Meta({
  name: 'TestDocument',
  fields(fields) {
    return _.extend(fields, {
      userPermissions: [
        {
          user: Document.ReferenceField(User, {_id: 1}),
          addedBy: Document.ReferenceField(User, {_id: 1}, false),
        },
      ],
    });
  },
});

User.documents.remove({_id: {$in: ['aaaa', 'bbbb']}});
TestDocument.documents.remove({_id: 'cccc'});

const WAIT_FOR_DATABASE_TIMEOUT = 1500; // ms

function waitForDatabase() {
  const future = new Future();

  let timeout = null;
  const newTimeout = function () {
    if (timeout) {
      Meteor.clearTimeout(timeout);
    }
    timeout = Meteor.setTimeout(function () {
      timeout = null;
      if (!future.isResolved()) {
        future.return();
      }
    }, WAIT_FOR_DATABASE_TIMEOUT);
  };

  newTimeout();

  const handles = [];
  for (const document of Document.list) {
    handles.push(document.documents.find({}).observeChanges({
      added(id, fields) {
        newTimeout();
      },
      changed(id, fields) {
        newTimeout();
      },
      removed(id) {
        newTimeout();
      },
    }));
  }

  future.wait();

  for (const handle of handles) {
    handle.stop();
  }
}

Meteor.startup(function () {
  User.documents.insert({
    _id: 'aaaa',
  });
  User.documents.insert({
    _id: 'bbbb',
  });

  TestDocument.documents.insert({
    _id: 'cccc',
    userPermissions: [
      {
        user: {
          _id: 'aaaa',
        },
        addedBy: {
          _id: 'aaaa',
        },
      },
      {
        user: {
          _id: 'bbbb',
        },
        addedBy: {
          _id: 'aaaa',
        },
      },
    ],
  });

  waitForDatabase();

  User.documents.remove({_id: 'aaaa'});
});

When this is ran the following error is seen:

Document 'TestDocument' 'cccc' field 'userPermissions.addedBy' is referencing a nonexistent document 'aaaa'

This is because subdocument with user_.id == 'aaaa' is pulled from the array, which triggers an update with subdocument with addedBy._id === 'aaaa' still there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant