meteor-publish-composite icon indicating copy to clipboard operation
meteor-publish-composite copied to clipboard

Parent is not always changed when updating a grand parent and a parent of a child

Open ericm546 opened this issue 8 years ago • 0 comments

If I have a meteor-publish-composite having a grand parent, a parent and a child and I do a mongo update to the grand parent and the parent., the parent is not always updated on the client. ex.

Meteor.publishComposite("parentsAndChilds", filterByUser => ({
  find() {
    return Parents.find();
  },
  children: [
    {
      find(parent) {
        return Childs.find({ _id: { $in: parent.childs } });
      },
      children: [
        {
          find(child) {
            //fake a sub children that take time to load
            Meteor._sleepForMs(100);
            return Childs.find({ _id: { $in: [] } });
          }
        }
      ]
    }
  ]
}));

Meteor.methods({
  updateRandomString(childId) {
    Parents.update(
      { childs: childId },
      { $set: { name: Random.id() } },
      { multi: true }
    );
    Meteor._sleepForMs(50);
    Childs.update({ _id: childId }, { $set: { name: Random.id() } });
  }
});

the problem happen in publication.js

        this.observeHandle = this.cursor.observe({
            added: Meteor.bindEnvironment((doc) => {
                const alreadyPublished = this.publishedDocs.has(doc._id);

                if (alreadyPublished) {
                    debugLog('Publication.observeHandle.added', `${collectionName}:${doc._id} already published`);
                    this.publishedDocs.unflagForRemoval(doc._id);
                    this._republishChildrenOf(doc);
                    this.subscription.changed(collectionName, doc._id, doc);
                } else {
                    this.publishedDocs.add(collectionName, doc._id);
                    this._publishChildrenOf(doc);
                    this.subscription.added(collectionName, doc);
                }
            }),
            changed: Meteor.bindEnvironment((newDoc) => {
                debugLog('Publication.observeHandle.changed', `${collectionName}:${newDoc._id}`);
                this._republishChildrenOf(newDoc);
            }),
            removed: (doc) => {
                debugLog('Publication.observeHandle.removed', `${collectionName}:${doc._id}`);
                this._removeDoc(collectionName, doc._id);
            },
        });

        this.observeChangesHandle = this.cursor.observeChanges({
            changed: (id, fields) => {
                debugLog('Publication.observeChangesHandle.changed', `${collectionName}:${id}`);
                this.subscription.changed(collectionName, id, fields);
            },
        });

In my example, 1 Parents.update is executed 2. _republishChildrenOf is called and the publish function of "Childs" is called 3. the code go through 'Publication.observeHandle.added', ... already published and _republishChildrenOf of "Childs" is called. 4. this execute my code " //fake a sub children that take time to load" 5. during the sleep of 100 ms, the code after the sleep of 50ms is executed and Childs.update is called 6. this would normally called the function this.observeChangesHandle for the "Childs" subscription but, as the code in the function publish is still waiting for the " //fake a sub children that take time to load", this.cursor.observeChanges is never called and Childs is never updated in the client.

Changing the order of this.observeHandle and this.observeChangesHandle in "publication.js" fixe the bug .

        this.observeChangesHandle = this.cursor.observeChanges({
            changed: (id, fields) => {
                debugLog('Publication.observeChangesHandle.changed', `${collectionName}:${id}`);
                this.subscription.changed(collectionName, id, fields);
            },
        });

        this.observeHandle = this.cursor.observe({
            added: Meteor.bindEnvironment((doc) => {
                const alreadyPublished = this.publishedDocs.has(doc._id);

                if (alreadyPublished) {
                    debugLog('Publication.observeHandle.added', `${collectionName}:${doc._id} already published`);
                    this.publishedDocs.unflagForRemoval(doc._id);
                    this._republishChildrenOf(doc);
                    this.subscription.changed(collectionName, doc._id, doc);
                } else {
                    this.publishedDocs.add(collectionName, doc._id);
                    this._publishChildrenOf(doc);
                    this.subscription.added(collectionName, doc);
                }
            }),
            changed: Meteor.bindEnvironment((newDoc) => {
                debugLog('Publication.observeHandle.changed', `${collectionName}:${newDoc._id}`);
                this._republishChildrenOf(newDoc);
            }),
            removed: (doc) => {
                debugLog('Publication.observeHandle.removed', `${collectionName}:${doc._id}`);
                this._removeDoc(collectionName, doc._id);
            },
        });

I have created a project to reproduce the bug.

git clone https://github.com/ericm546/bug-publish-composite.git
cd bug-publish-composite
npm install
meteor

Clicking on a button update the random name of the element and his parent, but only the parent is changed in the browser.

the important code is in /server/main.js

ericm546 avatar May 31 '17 23:05 ericm546