Client not receiving new subscriptions from publish.

300 views
Skip to first unread message

Bjorn Tipling

unread,
Jan 6, 2013, 12:00:39 AM1/6/13
to meteo...@googlegroups.com
I'm autosubscribing to a collection defined both on the client and the server via the same code. 

I am console logging the subscription and the fields I specify in the Meteor.autosubscribe function. I see that it is correct.

The publish observes changes for the collection and the fields. I am observing both changes and additions. I console log there and I see that it updates correctly with the fields I want.

I then respond to user input which makes a method call which updates the collection I am observing and subscribed to and publishing and make changes. I console log those changes, and I see that they are correct.

I then autorun with a function for changes on this collection, using the same fields or all fields or anything. I console log here and I see it publish nothing ever. Same collection, fields or not, whatever I do nothing happens.

I search the collection via webinspector it is not uptodate.

The subscription is correct I know that it is so.
The changes are made in the db, I know that it is so, I see the console log statements.
The publish is publishing on changes, I see that it is so.

But the client does not get the data!

When I refresh the page I get the data!

It only breaks with this collection! Everywhere else this works perfectly.

What could possibly be the matter? I am about to start deleting everything in my app until I have I can reduce it to the least common reproducible point. 

I have wasted hours on this already. It makes no sense.

Any help is appreciated. Thank you.

Bjorn


Tom Coleman

unread,
Jan 6, 2013, 12:04:06 AM1/6/13
to meteo...@googlegroups.com
Hi Bjorn,

Can you show the code for your publish function?

It sounds like you are doing something non-standard ("The publish observes changes for the collection and the fields. I am observing both changes and additions. I console log there and I see that it updates correctly with the fields I want") -- which is fine, but hard to reason about without seeing the code.
--
 
 

Bjorn Tipling

unread,
Jan 6, 2013, 9:54:42 AM1/6/13
to meteo...@googlegroups.com
I have reduced the code quit a bit. In addition to the bug, I see another one. The 'added' method on observe is called all the time even when no new documents are being added. Not sure what that is about.

Either I just don't understand something fundamental or Meteor.js has some bugs. 


Code:


Little video I made:


Client side logs:

checking test result sphela.js:10
no test undefined sphela.js:13
Tests updated! Object {_id: "a67cd1be-65ce-4044-80ca-4baaa3765994", testCount: 0} sphela.js:27
checking test result sphela.js:10
test.testCount 0 sphela.js:16
Running test. sphela.js:81
Updating test to 1. sphela.js:84
Test ran. Object {_id: "a67cd1be-65ce-4044-80ca-4baaa3765994", testCount: 1} sphela.js:86
Tests updated! Object {_id: "a67cd1be-65ce-4044-80ca-4baaa3765994", testCount: 1} sphela.js:27
checking test result sphela.js:10
test.testCount 1 sphela.js:16
Tests updated! Object {_id: "a67cd1be-65ce-4044-80ca-4baaa3765994", testCount: 0} sphela.js:27
checking test result sphela.js:10
test.testCount 0

Server side logs:

$ meteor reset; meteor
Project reset.
[[[[[ ~/Dropbox/projects/sphela-game/sphela ]]]]]

inserting new test
Connecting test, getting initial data.
Test added do nothing { testCount: 0, _id: 'a67cd1be-65ce-4044-80ca-4baaa3765994' }
Running test.
Updating test to 1.
Test ran. { testCount: 1, _id: 'a67cd1be-65ce-4044-80ca-4baaa3765994' }
Test added do nothing { testCount: 1, _id: 'a67cd1be-65ce-4044-80ca-4baaa3765994' }
Connecting test, getting initial data.


Or in its entirety:

<head>
  <title>Testing counts.</title>
</head>

<body>
  {{> app}}
</body>

<template name="app">
  {{#if testSuccess}}
    <h1>Test complete.</h1>
  {{else}}
    <button class="btn run-test">Run Test</button>
  {{/if}}
</template>


var Tests = new Meteor.Collection('tests');

if (Meteor.isClient) {
  Meteor.startup(function() {
    Session.set('testCount', 0)
    Meteor.subscribe('connect');
  });
  Template.app.testSuccess = function() {
    var test;
    console.log('checking test result');
    test = Tests.findOne();
    if (!test) {
      console.log('no test', test);
      return false;
    }
    console.log('test.testCount', test.testCount);
    return test.testCount > 0;
  };
  Template.app.events({
    'click .run-test': runTest
  });
  function runTest(event) {
    Meteor.call('runTest');
    Session.set('testCount', 1);
  }
  Meteor.autorun(function() {
    console.log('Tests updated!', Tests.findOne());
  });
  Meteor.autosubscribe(function() {
    Meteor.subscribe('test-results', Session.get('testCount'));
  });
}

if (Meteor.isServer) {
  Meteor.startup(function() {
    test = Tests.findOne({})
    if (!test) {
      test = {
        testCount: 0
      };
      console.log('inserting new test');
      test._id = Tests.insert(test);
    } else {
      console.log('startup reset');
      test.testCount = 0;
      Tests.update({_id:test._id}, test);
    }
  });

  Meteor.publish('connect', function() {
    var test_;
    console.log('Connecting test, getting initial data.');
    test_ = Tests.findOne({});
    this.set('tests', test_._id, test_);
    this.complete();
    this.flush();
  });

  Meteor.publish('test-results', function(test) {
    var handle;
    handle = Tests.find({testCount: test}).observe({
      changed: _.bind(function(test) {
        console.log('Test changed', test._id, test.testCount);
        this.set('tests', test._id, test);
        this.flush();
      }, this),
      added: _.bind(function(test) {
        console.log('Test added do nothing', test);
      }, this)
    });
    this.complete();
    this.flush();
    this.onStop(function() {
      handle.stop();
    });
  });
}
Meteor.methods({
  runTest: function() {
    var test;
    console.log('Running test.');
    test = Tests.findOne({});
    test.testCount = 1;
    console.log('Updating test to 1.');
    Tests.update({_id: test._id}, test);
    console.log('Test ran.', Tests.findOne());
  },
});

Bjorn Tipling

unread,
Jan 6, 2013, 10:13:00 AM1/6/13
to meteo...@googlegroups.com
Yes there is some kind of bug in meteor.js regarding autosubscribe, observing changes and adding.

I only ever set the testCount to 0 on startup and I log it when I do. And I autosubscribe to the session variable testCount. Why is the data being reset on the front-end to show testCount 0? Why is console logging adding when I only insert after I reset meteor and log when I do yet see no log messages on the server?

The session variable for testCount gets set to 1 and yet it still updates my local client data store back to a test with testCount 0, it makes no sense (no where on the frontend do I ever set the testCount to anything, and you can see from the test _id that it's always the same document. I pushed this code to meteor.com and saw the same bug. Can't explain it:

Tests updated! undefined sphela.js:27
checking test result sphela.js:10
no test undefined sphela.js:13
Tests updated! Object {_id: "6b15e732-3e87-4dbc-8bb6-2dedf586c403", testCount: 0} sphela.js:27
checking test result sphela.js:10
test.testCount 0 sphela.js:16
Session.get('testCount')
0
Running test. sphela.js:82
Updating test to 1. sphela.js:85
Test ran. Object {_id: "6b15e732-3e87-4dbc-8bb6-2dedf586c403", testCount: 1} sphela.js:87
Tests updated! Object {_id: "6b15e732-3e87-4dbc-8bb6-2dedf586c403", testCount: 1} sphela.js:27
checking test result sphela.js:10
test.testCount 1 sphela.js:16
Tests updated! Object {_id: "6b15e732-3e87-4dbc-8bb6-2dedf586c403", testCount: 0} sphela.js:27
checking test result sphela.js:10
test.testCount 0 sphela.js:16
Session.get('testCount')
1
Tests.find().forEach(function(test) { console.log('test', test); });
test Object {_id: "6b15e732-3e87-4dbc-8bb6-2dedf586c403", testCount: 0}
undefined

Bjorn Tipling

unread,
Jan 6, 2013, 10:59:32 AM1/6/13
to meteo...@googlegroups.com
I accidentally moved the methods outside the isServer if and that led to the method call being simulated. When I put it inside I never receive any updates for Tests.


server logs:

$ meteor reset;meteor
Project reset.
[[[[[ ~/Dropbox/projects/sphela-game/sphela ]]]]]

inserting new test
Connecting test, getting initial data.
Test added do nothing { testCount: 0, _id: 'ea9f6002-74c7-4f37-9f10-0167b3b6f65a' }
Connecting test, getting initial data.
Test added do nothing { testCount: 0, _id: 'ea9f6002-74c7-4f37-9f10-0167b3b6f65a' }
Running test.
Updating test to 1.
Test ran. { testCount: 1, _id: 'ea9f6002-74c7-4f37-9f10-0167b3b6f65a' }
Test added do nothing { testCount: 1, _id: 'ea9f6002-74c7-4f37-9f10-0167b3b6f65a' }

Tests updated! undefined sphela.js:27
checking test result sphela.js:10
no test undefined sphela.js:13
Tests updated! Object {_id: "ea9f6002-74c7-4f37-9f10-0167b3b6f65a", testCount: 0} sphela.js:27
checking test result sphela.js:10
test.testCount 0

Naomi Seyfer

unread,
Jan 6, 2013, 1:20:29 PM1/6/13
to meteo...@googlegroups.com
This is a great question, and the behavior you're seeing gets into some details of writing custom publishers based on observes in Meteor.

It seems to me that what you're seeing is a consequence of how multiple subscriptions work.  Here's what I think is happening.
  • Initial publish, server sends down "in collection test, there's an object with id ea9f6002-74c7-4f37-9f10-0167b3b6f65a where testCount is 0"
  • Session variable count is 0, so we subscribe test-results where testCount is 0.  Great, our local version of the database already thinks that, all subscriptions agree, no change is necessary.
  • Session variable count gets updated to 1.  Autosubscribe goes and subscribes to test-results were testCount is 1.  This means that now we have two publishers going:
    • 'connect' thinks ea9f6002-74c7-4f37-9f10-0167b3b6f65a has a testCount of 0 (it never gets updated, how could it think differently?
    • 'test-results' gets an 'added' message to say ea9f6002-74c7-4f37-9f10-0167b3b6f65a has a testCount of 1 (when you run a new observe, you get an 'added' message for everything in it.  Later on, you get a 'changed' message when things that match that cursor change) -- but it does nothing with the added message.  It looks like you were expecting a 'changed' message instead.
  • The overall result here is that since we have one publisher saying ea9f6002-74c7-4f37-9f10-0167b3b6f65a has a testCount of 0, and no publisher saying anything else about it, the client thinks ea9f6002-74c7-4f37-9f10-0167b3b6f65a has a testCount of 0.

To be sure the client sees that the testCount was non-0, all the publishers that assert anything about document ea9f6002-74c7-4f37-9f10-0167b3b6f65a in the collection must say that testCount is non-0, with the appropriate set and flush calls.  Even if you sent the message that testCount was 1 in the added callback of the observe, Meteor doesn't guarantee anything about what the client sees when it is subscribed to two different things that conflict about the value of a document.

tl;dr version: 
  • Observing a cursor gets the added callback once for everything in the observed set whenever it is first run, even if the items were in the database already.
  • When you have multiple publishers publishing the same keys in the same documents, the version the client sees is going to be one of them, but Meteor makes no guarantees as to which one if the publishers disagree. Try not to allow multiple publishers to disagree about the contents of documents, it can only cause confusion.

matt debergalis

unread,
Jan 6, 2013, 1:48:53 PM1/6/13
to meteo...@googlegroups.com
We used to hard-wire the merge box to resolve conflicting values in
favor of the subscription that started earlier. Is that not the case
now?

From http://docs.meteor.com/#meteor_subscribe :

If more than one subscription sends conflicting values for an
attribute (same collection name, document ID, and attribute name),
then the value on the client will be that from the first subscription
the client activated. (Even if it is not the first to send the
duplicated attribute.)

> When you have multiple publishers publishing the same keys in the same
> documents, the version the client sees is going to be one of them, but
> Meteor makes no guarantees as to which one if the publishers disagree. Try
> not to allow multiple publishers to disagree about the contents of
> documents, it can only cause confusion.
>
> --
>
>

Naomi Seyfer

unread,
Jan 6, 2013, 1:55:15 PM1/6/13
to meteo...@googlegroups.com
It's true that 0.5.2 has the guarantee you describe, but we're changing the behavior in future Meteor (0.5.4 or later) to be slightly different (the first assertion made wins, not the first subscription to make an assertion), and changing the docs to leave ourselves a little more wiggle room for implementation in the future, as long as we do our best to preserve the "subscriptions don't unnecessarily fluctuate" property.

matt debergalis

unread,
Jan 6, 2013, 2:13:22 PM1/6/13
to meteo...@googlegroups.com
Cool.
> --
>
>

Bjorn Tipling

unread,
Jan 6, 2013, 4:07:34 PM1/6/13
to meteo...@googlegroups.com
Hi Naomi,

This was it. Once I removed the subscription for 'connect' and added a this.set method back into the added handler everything worked as expected. I completely missed the part of the documentation Matt pointed out:

> If more than one subscription sends conflicting values for an attribute (same collection name, document ID, and attribute name), 
> then the value on the client will be that from the first subscription the client activated. 
> (Even if it is not the first to send the duplicated attribute.) 

I also misunderstood what was meant by the added handler documentation: 

> A new document entered the result set.

I mistakenly understood this to mean whenever a document was added to the Collection.

Thank you for taking the time to explain this.

Bjorn
Reply all
Reply to author
Forward
0 new messages