matb33
matb33

Reputation: 2820

Possible Meteor bug in manual publish/subscribe scenario

I am encountering a problem with subscribing/publishing in Meteor. I've written an example Meteor app to help narrow the scope of the problem.

I am publishing a collection on the server that is filtered by a parameter passed in through a subscription on the client. This subscription is within an autosubscribe, which leverages a session variable to reactively update the subscriptions.

When changing the state of this particular session variable, the collection on the client isn't getting updated properly, or at least that's what I gather. I've spent the whole day on this and have not found an issue in code I control. I suspect I'm either not understanding how to setup proper pub-sub in Meteor, or there's a problem within Meteor.

To reproduce the problem, start a new Meteor project and use the following (Make sure to remove the autopublish package when trying it out):

HTML (test.html for example):

<head>
    <title>pubsubbug</title>
</head>

<body>
    {{> main}}
</body>

<template name="main">
    <h1>Example showing possible bug in Meteor wrt pub-sub</h1>
    <p><button name="showall">show all ({{showall}})</button></p>
    <div style="float:left;width:400px;">
        <h2>Notes:</h2>
        <ul>
            {{#each notes}}
                <li>{{title}}</li>
            {{/each}}
        </ul>
    </div>
    <div style="float:left;">
        <h2>Notes (copied):</h2>
        <ul>
            {{#each notes_copied}}
                <li>{{title}}</li>
            {{/each}}
        </ul>
    </div>
</template>

JS (test.js for example)

if (Meteor.is_client) {
    Notes = new Meteor.Collection("notes_collection");
    NotesCopied = new Meteor.Collection("notes_collection_copied");

    Session.set("showall", false);

    Meteor.autosubscribe(function () {
        Meteor.subscribe("notes_subscription", Session.get("showall"), function () {
            console.log("Notes count:", Notes.find().count());
        });

        Meteor.subscribe("notes_subscription_copied", Session.get("showall"), function () {
            console.log("Bug? This isn't getting called.");
            console.log("NotesCopied count:", NotesCopied.find().count());
        });
    });

    Template.main.notes = function () {
        return Notes.find();
    };

    Template.main.notes_copied = function () {
        return NotesCopied.find();
    };

    Template.main.showall = function () {
        return Session.get("showall");
    };

    Template.main.events = {
        "click button[name='showall']": function (evt) {
            Session.set("showall", !Session.get("showall"));
        }
    };
}

if (Meteor.is_server) {
    Notes = new Meteor.Collection("notes_collection");

    var getNotes = function (showall) {
        if (showall) {
            return Notes.find({}, {sort: {title: 1}});
        } else {
            return Notes.find({visible: true}, {sort: {title: 1}});
        }
    };

    Meteor.publish("notes_subscription", function (showall) {
        // By sending the Notes back with the same uuid as before, the
        // client end seems to get confused:
        return getNotes(showall);
    });

    Meteor.publish("notes_subscription_copied", function (showall) {
        var notes = getNotes(showall);
        var self = this;

        // Copy notes into a new notes collection (see NotesCopied on client).
        // By generating a new uuid, we don't get an issue with the notes
        // on the client getting screwed up:
        notes.forEach(function (note) {
            var uuid = Meteor.uuid();   // note._id will cause same problem
            self.set("notes_collection_copied", uuid, {title: note.title});
        });

        self.flush();
        self.complete();
    });

    // Add example notes
    Meteor.startup(function () {
        if (Notes.find().count() === 0) {
            Notes.insert({title: "Note #1 (always visible)", visible: true});
            Notes.insert({title: "Note #2 (always visible)", visible: true});
            Notes.insert({title: "Note #3 (always visible)", visible: true});
            Notes.insert({title: "Note #4 (only visible when showall true)", visible: false});
            Notes.insert({title: "Note #5 (only visible when showall true)", visible: false});
            Notes.insert({title: "Note #6 (only visible when showall true)", visible: false});
        }
    });
}

An explanation of what you will be seeing:

There will be a button that, when clicked, simply toggles a session variable (showall) between true and false.

Two subscriptions exist (within an autosubscribe), one that exemplifies the bug, and another that is suffixed with _copied, which is a test to demonstrate that when the collection in question is "copied" and new uuid's are assigned, the results are displayed properly. I couldn't figure out what to do with this particular bit of info... I don't want new uuid's.

So basically, when the show all button is clicked repeatedly, the first column Notes: will display incorrect results, and after a few clicks won't show anything.

On the other hand, the second column Notes (copied):, whose uuid's are re-generated each time, shows up correctly.

Is this a bug? Or is there a proper way to do this?

EDIT: Example above live over at http://pubsubbug.meteor.com/

Upvotes: 1

Views: 691

Answers (1)

Tamara Wijsman
Tamara Wijsman

Reputation: 12348

Not experiencing your bug on the developer branch on Windows. Since this is the case, it is a good sign that there is nothing wrong with your code. It appears that you see something buggy regarding the subscriptions and/or how Mongo queries.

Meteor itself is most likely running the stable (= master) release on their hosting, so you will have to try a different approach or wait for a new release. Unless you can support running on devel...

Upvotes: 2

Related Questions