Multiple subscriptions don't work on same document with different set of fields #3259

Closed
aramshat opened this Issue Dec 8, 2014 · 32 comments

Comments

Projects
None yet
10 participants
@aramshat

aramshat commented Dec 8, 2014

For example there is a collection C and client is subscribed to C.find({}, {fields: {a: true, b: true}}). Then after some event client subscribes to C.find({_id: 'some_id'}, {fields: {a: true, b: true, c: true}}). In that case subscription is not updated, i.e. the field c is not available to client. It seems that wider subscription is not working on a document to which there is already an older subscription with less fields.

@steph643

This comment has been minimized.

Show comment
Hide comment
@steph643

steph643 Dec 8, 2014

@aram90, unless I don't understand your case, I am using this in my application without any problem (Meteor 0.8.3).

steph643 commented Dec 8, 2014

@aram90, unless I don't understand your case, I am using this in my application without any problem (Meteor 0.8.3).

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 8, 2014

@steph643 are you using subscriptions from Deps.autorun or manually? I have experienced same issue few times with different subscriptions so I guess that's not a problem with one specific subscription.

aramshat commented Dec 8, 2014

@steph643 are you using subscriptions from Deps.autorun or manually? I have experienced same issue few times with different subscriptions so I guess that's not a problem with one specific subscription.

@steph643

This comment has been minimized.

Show comment
Hide comment
@steph643

steph643 Dec 8, 2014

I always use subscriptions from Deps.autorun (or rather this.autorun in templates), so that they are updated automatically when reactive parameters of my publish functions change.
Maybe you could share your publish function signatures and subscribe calls?

steph643 commented Dec 8, 2014

I always use subscriptions from Deps.autorun (or rather this.autorun in templates), so that they are updated automatically when reactive parameters of my publish functions change.
Maybe you could share your publish function signatures and subscribe calls?

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

Here is my structure (using equivalent to actual source code):

Meteor.publish('currentDoc', function() {
  return myDocs.find({userId: this.userId}, {fields: {prop1: true, prop2: true, prop3: true}});
});

Meteor.publish('allDocs', function() {
  return myDocs.find({}, {fields: {prop1: true, prop2: true}});
});

So basically I have two different publish methods, one publishing all documents with few fields, and other one publishing one document with userId filter returning an extra field (prop3).
Now here are subscriptions:

Deps.autorun(function() {
    if(getCurrentPage() == 'page1') {
        Meteor.subscribe('currentDoc');
    }
});

Deps.autorun(function() {
    if(getCurrentPage() == 'page2') {
        Meteor.subscribe('allDocs');
    }
});

getCurrentPage() has a reactive dependency from iron router, so these autoruns are being called every time I navigate from one page to another. When I'm on page2, I see all documents in miniMongo collection having prop1 and prop2 fields as expected. When I navigate to page1 from there, miniMongo collection is updated to contain only document of current user as expected, however it is still missing prop3 field. After refreshing the page page1, it starts to work as expected, i.e. prop3 starts to show up.

aramshat commented Dec 9, 2014

Here is my structure (using equivalent to actual source code):

Meteor.publish('currentDoc', function() {
  return myDocs.find({userId: this.userId}, {fields: {prop1: true, prop2: true, prop3: true}});
});

Meteor.publish('allDocs', function() {
  return myDocs.find({}, {fields: {prop1: true, prop2: true}});
});

So basically I have two different publish methods, one publishing all documents with few fields, and other one publishing one document with userId filter returning an extra field (prop3).
Now here are subscriptions:

Deps.autorun(function() {
    if(getCurrentPage() == 'page1') {
        Meteor.subscribe('currentDoc');
    }
});

Deps.autorun(function() {
    if(getCurrentPage() == 'page2') {
        Meteor.subscribe('allDocs');
    }
});

getCurrentPage() has a reactive dependency from iron router, so these autoruns are being called every time I navigate from one page to another. When I'm on page2, I see all documents in miniMongo collection having prop1 and prop2 fields as expected. When I navigate to page1 from there, miniMongo collection is updated to contain only document of current user as expected, however it is still missing prop3 field. After refreshing the page page1, it starts to work as expected, i.e. prop3 starts to show up.

@PeppeL-G

This comment has been minimized.

Show comment
Hide comment
@PeppeL-G

PeppeL-G Dec 9, 2014

You've made a mistake in your allDocs publish. The fields option should have an object as value.

PeppeL-G commented Dec 9, 2014

You've made a mistake in your allDocs publish. The fields option should have an object as value.

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

@PeppeL-G thanks for the catch, I have fixed the typo, in original source code it is correct.

aramshat commented Dec 9, 2014

@PeppeL-G thanks for the catch, I have fixed the typo, in original source code it is correct.

@steph643

This comment has been minimized.

Show comment
Hide comment
@steph643

steph643 Dec 9, 2014

I guess the error in your allDocs publish is a typo.
I am surprised about the behavior you describe: "When I navigate to page1 from there, miniMongo collection is updated to contain only document of current user as expected". That is not what I would expect. Unless you somehow stop allDocs subscribe (which I don't see in your code), all documents should stay in minimongo.
Are you sure the above pseudo-code reflects your real code?

steph643 commented Dec 9, 2014

I guess the error in your allDocs publish is a typo.
I am surprised about the behavior you describe: "When I navigate to page1 from there, miniMongo collection is updated to contain only document of current user as expected". That is not what I would expect. Unless you somehow stop allDocs subscribe (which I don't see in your code), all documents should stay in minimongo.
Are you sure the above pseudo-code reflects your real code?

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

@steph643 as noted in message above that typo is only here, in original source code it is correct. Regarding stopping the subscription - since it is subscribed from Deps.autorun it is automatically updated when reactive data source (in this case getCurrentPage) changes, so subscription to all docs is automatically stopped when navigating away from page2.

aramshat commented Dec 9, 2014

@steph643 as noted in message above that typo is only here, in original source code it is correct. Regarding stopping the subscription - since it is subscribed from Deps.autorun it is automatically updated when reactive data source (in this case getCurrentPage) changes, so subscription to all docs is automatically stopped when navigating away from page2.

@steph643

This comment has been minimized.

Show comment
Hide comment
@steph643

steph643 Dec 9, 2014

Unless I am missing something, you don't call Meteor.subscribe('allDocs') when you navigate away from page2. So I don't see how the subscription would be stopped.

steph643 commented Dec 9, 2014

Unless I am missing something, you don't call Meteor.subscribe('allDocs') when you navigate away from page2. So I don't see how the subscription would be stopped.

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

From meteor docs:

If you call Meteor.subscribe inside Tracker.autorun, the subscription will be cancelled automatically whenever the computation reruns (so that a new subscription can be created, if appropriate), meaning you don't have to to call stop on subscriptions made from inside Tracker.autorun.

Deps.autorun I'm using works in that same way.

aramshat commented Dec 9, 2014

From meteor docs:

If you call Meteor.subscribe inside Tracker.autorun, the subscription will be cancelled automatically whenever the computation reruns (so that a new subscription can be created, if appropriate), meaning you don't have to to call stop on subscriptions made from inside Tracker.autorun.

Deps.autorun I'm using works in that same way.

@aramshat aramshat changed the title from Multiple subscriptions doesn't work on same document with different set of fields to Multiple subscriptions don't work on same document with different set of fields Dec 9, 2014

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

Also this is easy to reproduce on any meteor project. Just create 2 publish functions for Meteor.users collection, for example:

Meteor.publish('usersBasicData', function(userIds) {
  return Meteor.users.find({_id: {$in: userIds}}, {fields: {_id: true}});
});

Meteor.publish('usersData', function(userIds) {
  return Meteor.users.find({_id: {$in: userIds}}, {fields: {_id: true, username: true}});
});

Now call:

Meteor.subscribe('usersBasicData', ['someUserId123']);

You will see only _id field in miniMongo as expected. After that try calling

Meteor.subscribe('usersData', ['someUserId123']);

Nothing changes, only existing field in miniMongo is still _id.
But if you call usersData subscription first after refreshing the page, you can see both _id and username fields as expected.

aramshat commented Dec 9, 2014

Also this is easy to reproduce on any meteor project. Just create 2 publish functions for Meteor.users collection, for example:

Meteor.publish('usersBasicData', function(userIds) {
  return Meteor.users.find({_id: {$in: userIds}}, {fields: {_id: true}});
});

Meteor.publish('usersData', function(userIds) {
  return Meteor.users.find({_id: {$in: userIds}}, {fields: {_id: true, username: true}});
});

Now call:

Meteor.subscribe('usersBasicData', ['someUserId123']);

You will see only _id field in miniMongo as expected. After that try calling

Meteor.subscribe('usersData', ['someUserId123']);

Nothing changes, only existing field in miniMongo is still _id.
But if you call usersData subscription first after refreshing the page, you can see both _id and username fields as expected.

@steph643

This comment has been minimized.

Show comment
Hide comment
@steph643

steph643 Dec 9, 2014

Yes, good point.

How do check that your data is loaded after calling subscribe? Do you use the onReady() callback or subscription.ready()?

On another side, please allow me for some remarks about your way of doing, in case this can help you finding a workaround to your problem:

  • I don't think it is optimal to stop one subscription and immediately start another one on the same Collection, because some documents will be removed from minimongo and loaded again. I think this can be solved either by putting the 2 subscribes in the same autorun or by merging your 2 publish functions into one.
  • You might want to use Iron-router hooks instead of Tracker.autorun. See also Iron-router subscription management.

steph643 commented Dec 9, 2014

Yes, good point.

How do check that your data is loaded after calling subscribe? Do you use the onReady() callback or subscription.ready()?

On another side, please allow me for some remarks about your way of doing, in case this can help you finding a workaround to your problem:

  • I don't think it is optimal to stop one subscription and immediately start another one on the same Collection, because some documents will be removed from minimongo and loaded again. I think this can be solved either by putting the 2 subscribes in the same autorun or by merging your 2 publish functions into one.
  • You might want to use Iron-router hooks instead of Tracker.autorun. See also Iron-router subscription management.
@PeppeL-G

This comment has been minimized.

Show comment
Hide comment
@PeppeL-G

PeppeL-G Dec 9, 2014

According to the docs, you should use 1 instead of true. Does changing that make it work?

PeppeL-G commented Dec 9, 2014

According to the docs, you should use 1 instead of true. Does changing that make it work?

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

@steph643 I'm printing its state and it shows that subscription is ready, but fields are still not being populated. Regarding iron-router hooks I stopped using them after noticing that they are being automatically called multiple times because of reactive dependencies inside.

@PeppeL-G no it doesn't make any difference.

aramshat commented Dec 9, 2014

@steph643 I'm printing its state and it shows that subscription is ready, but fields are still not being populated. Regarding iron-router hooks I stopped using them after noticing that they are being automatically called multiple times because of reactive dependencies inside.

@PeppeL-G no it doesn't make any difference.

@steph643

This comment has been minimized.

Show comment
Hide comment
@steph643

steph643 Dec 9, 2014

Please have a look at issue #3194.
Does the suggested workaround (using setTimeout(0) before reading any data) solve your problem?

steph643 commented Dec 9, 2014

Please have a look at issue #3194.
Does the suggested workaround (using setTimeout(0) before reading any data) solve your problem?

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

My issue is not related to a delay like noted in the one you've specified, I'm checking miniMongo collection manually from JS console few seconds after subscription and records are never being populated.

aramshat commented Dec 9, 2014

My issue is not related to a delay like noted in the one you've specified, I'm checking miniMongo collection manually from JS console few seconds after subscription and records are never being populated.

@steph643

This comment has been minimized.

Show comment
Hide comment
@steph643

steph643 Dec 9, 2014

Well, I got no more ideas.
Please submit a repo and I will try to reproduce on my side.

steph643 commented Dec 9, 2014

Well, I got no more ideas.
Please submit a repo and I will try to reproduce on my side.

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

@steph643 to reproduce the issue on a basic meteor project see my previous message - #3259 (comment)

aramshat commented Dec 9, 2014

@steph643 to reproduce the issue on a basic meteor project see my previous message - #3259 (comment)

@steph643

This comment has been minimized.

Show comment
Hide comment
@steph643

steph643 Dec 9, 2014

Well, I prefer you submit a repo. Two reasons for that (beyond the fact that I am lazy :-) :
1- you might find a solution while setting up the test case,
2- in the end, if it really is a bug, MDG won't fix it unless there is a repo.

steph643 commented Dec 9, 2014

Well, I prefer you submit a repo. Two reasons for that (beyond the fact that I am lazy :-) :
1- you might find a solution while setting up the test case,
2- in the end, if it really is a bug, MDG won't fix it unless there is a repo.

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Dec 9, 2014

Ok in that case I'll create & submit one containing example above.

aramshat commented Dec 9, 2014

Ok in that case I'll create & submit one containing example above.

@kostko

This comment has been minimized.

Show comment
Hide comment
@kostko

kostko Jan 4, 2015

Has there been any progress on this? I am experiencing the exact same issue.

kostko commented Jan 4, 2015

Has there been any progress on this? I am experiencing the exact same issue.

@mitar

This comment has been minimized.

Show comment
Hide comment
@mitar

mitar Jan 10, 2015

Collaborator

This definitely works and I am using it in my cases. But only top-level fields are merged. It does not work with subdocuments and this is a known Meteor limitation. But for top-level fields it works. Please create a small reproduction. See guidelines to how to prepare a reproduction and report bugs.

Collaborator

mitar commented Jan 10, 2015

This definitely works and I am using it in my cases. But only top-level fields are merged. It does not work with subdocuments and this is a known Meteor limitation. But for top-level fields it works. Please create a small reproduction. See guidelines to how to prepare a reproduction and report bugs.

@glasser

This comment has been minimized.

Show comment
Hide comment
@glasser

glasser Jan 12, 2015

Member

As many of our community members have said, code snippets are not a reproduction. I'll be happy to re-open this if a full reproduction is included. https://github.com/meteor/meteor/wiki/Contributing-to-Meteor#reporting-a-bug-in-meteor

Member

glasser commented Jan 12, 2015

As many of our community members have said, code snippets are not a reproduction. I'll be happy to re-open this if a full reproduction is included. https://github.com/meteor/meteor/wiki/Contributing-to-Meteor#reporting-a-bug-in-meteor

@juliomac

This comment has been minimized.

Show comment
Hide comment
@juliomac

juliomac Aug 22, 2015

I have the exact same problem. Thanks @aram90 for bringing it up on this post. I spent hours trying to figure it out.

I have made a simple repo to show up the problem. Hope it can contribute to fix this issue.

It is at: https://github.com/juliomac/Bug-MultiSubs-DifSetofFields-3259

I have the exact same problem. Thanks @aram90 for bringing it up on this post. I spent hours trying to figure it out.

I have made a simple repo to show up the problem. Hope it can contribute to fix this issue.

It is at: https://github.com/juliomac/Bug-MultiSubs-DifSetofFields-3259

@mitar

This comment has been minimized.

Show comment
Hide comment
@mitar

mitar Aug 22, 2015

Collaborator

Reproduction repository is empty.

Collaborator

mitar commented Aug 22, 2015

Reproduction repository is empty.

@juliomac

This comment has been minimized.

Show comment
Hide comment
@juliomac

juliomac Aug 23, 2015

Ooops! Clicked upload too quickly. It is now up there. Sorry for it.

Ooops! Clicked upload too quickly. It is now up there. Sorry for it.

@mitar

This comment has been minimized.

Show comment
Hide comment
@mitar

mitar Aug 23, 2015

Collaborator

OK, this example has some basic issues. Like you cannot subscribe inside a template helper and then just expect that data will be available immediately afterwards? You should put your subscribe calls inside onCreated, see example.

Collaborator

mitar commented Aug 23, 2015

OK, this example has some basic issues. Like you cannot subscribe inside a template helper and then just expect that data will be available immediately afterwards? You should put your subscribe calls inside onCreated, see example.

@aramshat

This comment has been minimized.

Show comment
Hide comment
@aramshat

aramshat Aug 23, 2015

Actually I found that it is working for high level fields as @mitar noted, but in my case it was like stats.oneField and stats.*, so sub-fields didn't got merged. Logically I believe it should behave in the same way for both top-level fields and sub-fields.

Actually I found that it is working for high level fields as @mitar noted, but in my case it was like stats.oneField and stats.*, so sub-fields didn't got merged. Logically I believe it should behave in the same way for both top-level fields and sub-fields.

@juliomac

This comment has been minimized.

Show comment
Hide comment
@juliomac

juliomac Aug 24, 2015

The problem on my example was something else. There was a silly bug on it. The second field now shows up.

After all, this example failed to reproduce the issue. Also tested with sub-documents and they also have shown up after second publication. I will have to rethink how to reproduce it or figure out if it was something else on my real app code.

I also checked @mitar observation and subscribed at onCreated and back directly at helpers. Couldn't see a difference as It worked in both cases.

I posted the updated example on repo.

The problem on my example was something else. There was a silly bug on it. The second field now shows up.

After all, this example failed to reproduce the issue. Also tested with sub-documents and they also have shown up after second publication. I will have to rethink how to reproduce it or figure out if it was something else on my real app code.

I also checked @mitar observation and subscribed at onCreated and back directly at helpers. Couldn't see a difference as It worked in both cases.

I posted the updated example on repo.

@anothermohit

This comment has been minimized.

Show comment
Hide comment
@anothermohit

anothermohit Mar 25, 2016

For anyone else landing here, I can confirm that just the top level fields are getting merged on subsequent subscriptions but not nested ones. Probably because of this -

The most important consideration is related to the way DDP, Meteor’s data loading protocol, communicates documents over the wire. The key thing to realize is that DDP sends changes to documents at the level of top-level document fields.

Quoting from Meteor guide

For anyone else landing here, I can confirm that just the top level fields are getting merged on subsequent subscriptions but not nested ones. Probably because of this -

The most important consideration is related to the way DDP, Meteor’s data loading protocol, communicates documents over the wire. The key thing to realize is that DDP sends changes to documents at the level of top-level document fields.

Quoting from Meteor guide

@faizmh

This comment has been minimized.

Show comment
Hide comment
@faizmh

faizmh Jun 22, 2016

My symptoms
1)i have a student list-all page where I would subscribe to some fields of student collection
2)once i select anyone student, it goes individual student page. Here I subscribe to all fields
3)Some fields remain surprisingly blank[BUG]
4)console.log of Student shows all fields
5)Note - Student is not reactive by design

My technical analysis - websocket inspection
T1) On student list, all students were getting added ("msg":addded) with subscribed fields
T2) On going to individual student page, Student subscription with all fields was triggering
T3) Server would respond back "msg":changed and the individual Student data is available on the page
T4) I also noticed, remaining students are being removed ("msg":removed) by the server
T5) In between i also see another "msg":changed . This contain fields which are not part of T3 , but some of them are part of T1

My troubeshooting
M1. I triedto timeout my single Student subscription, until, server removes student-list subscription
and that worked. But I knew this was not a right solution
M2. I realized on T5, it had to do with nested projections

My final solution
In student list, i used nested projection. Once I removed the nested projection my issues were resovled.

Sequence of things
S1. in student list, i subscribe for list of students with {first_name:1, parent.name:1}
S2 in student page, i subscribe for {first_name,parent:1,address:1} // See how my subscribing for parent:1 and not parent.name:1 .... TADAAAA!!!!
S3. A "msg":changed with {address:data} is received
S4. A "msg":changed with {parent:data} is received

if in the above sequence of things, had i put {first_name:1, parent:1} in S1, then there would be no S4

Summary
This had to do a lot on how nested projections are handled in meteor sync.
There is an obvious element of network delay which can mess up things as well.
I believe if I had autorun or reactive helpers, I may not have caught this problem either.
There isn't way to capture S4 unless you have reactive variables

Finally learned meteor well now

faizmh commented Jun 22, 2016

My symptoms
1)i have a student list-all page where I would subscribe to some fields of student collection
2)once i select anyone student, it goes individual student page. Here I subscribe to all fields
3)Some fields remain surprisingly blank[BUG]
4)console.log of Student shows all fields
5)Note - Student is not reactive by design

My technical analysis - websocket inspection
T1) On student list, all students were getting added ("msg":addded) with subscribed fields
T2) On going to individual student page, Student subscription with all fields was triggering
T3) Server would respond back "msg":changed and the individual Student data is available on the page
T4) I also noticed, remaining students are being removed ("msg":removed) by the server
T5) In between i also see another "msg":changed . This contain fields which are not part of T3 , but some of them are part of T1

My troubeshooting
M1. I triedto timeout my single Student subscription, until, server removes student-list subscription
and that worked. But I knew this was not a right solution
M2. I realized on T5, it had to do with nested projections

My final solution
In student list, i used nested projection. Once I removed the nested projection my issues were resovled.

Sequence of things
S1. in student list, i subscribe for list of students with {first_name:1, parent.name:1}
S2 in student page, i subscribe for {first_name,parent:1,address:1} // See how my subscribing for parent:1 and not parent.name:1 .... TADAAAA!!!!
S3. A "msg":changed with {address:data} is received
S4. A "msg":changed with {parent:data} is received

if in the above sequence of things, had i put {first_name:1, parent:1} in S1, then there would be no S4

Summary
This had to do a lot on how nested projections are handled in meteor sync.
There is an obvious element of network delay which can mess up things as well.
I believe if I had autorun or reactive helpers, I may not have caught this problem either.
There isn't way to capture S4 unless you have reactive variables

Finally learned meteor well now

@sidzan

This comment has been minimized.

Show comment
Hide comment
@sidzan

sidzan Dec 27, 2017

Any one has solution to this ? This is till happening !

sidzan commented Dec 27, 2017

Any one has solution to this ? This is till happening !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment