Subscription management FAQ

Lot’s of questions are popping up about subscription management and I wanted to start listing some of the references and questions. Maybe generate a FAQ or something. Not sure this is the right place to do it but here it goes nonetheless:

Useful reading:

FAQ:

  • Subscribing to more data means the user is less likely to wait for loading while using the app but also means longer initial loading. Is it possible to do non-blocking subscriptions on data super-sets (e.g. all blog posts), and do blocking subscriptions for immediately required data (e.g. this blog post I am currently wanting to read)?
  • Created a meteorpad to show this does work fine http://meteorpad.com/pad/powp2CTWGAoyfb8va/Subscribe%20Unblock%20Test
  • Still an open question of how mergbox works in this scenario (e.g. where in theory adding data to mergbox and transfering data via DDP is interrupted to yield)
  • what happens if you do an unblocked subscription, followed immediately by the same subscription but blocked?
  • if you subscribe to publisher that returns a full collection, and then subscribe to a second publisher that returns a subset of that collection, is the server smart enough to know the superset has been sent to the client and immediately return ready?
    • Mergebox ensures no duplicate data is sent
    • this is only true for top level data in a collection (not nested data)
  • if you do identical subscriptions but in different contexts (e.g. one at the router level with Meteor.subscribe and one at the template level with this.subscribe) what happens?
    • Mergebox ensures no duplicate data is sent
    • Not sure about ready handlers??
  • if you do two subscriptions to the same publisher but pass a different value (e.g. Meteor.subscribe(“blogposts” blogPostId1) and Meteor.subscribe(“blogPosts” BlogpostId2) what happens?
  • Flow-router’s register subscription functionality provides the functionality to check subscription readiness via FlowRouter.subsReady() within an autorun. How would you replicate this without the use of flow-router?
    • see response from ccorcos below
  • what are some guidelines and things to watch out for when using a subscription manager?
  • from @elie can either or both of these subscriptions managers be used in template subscriptions? how?
  • borrowed from @mattkrick if two pages currently use the same subscription, this new template-level pattern means I would sub onCreated, stop onDestroyed, then sub again on the new page’s onCreated. This makes me wonder, would it be more efficient to just continue using a Meteor.subscribe if a subscription is used across multiple templates?
    • see response from ccros below
    • TL;DR depends on flush cycle - add more information about what determines a flush cycle and how to determine if adjecnt subscribes fall within the same cycle
  • if you do a this.subscribe() in an onCreated and it returns ready, and then later a new document is added to the collection returned by that subscription how does the “readiness” of that subscription change? Does it always return ready? is ready re-sent when the publish data changes? if so how can you catch that?
  • how do you handle subscribing in templates when the template depends on the data from multiple subscriptions and at least one of the subscriptions requires a parameter that can only be extracted from the data returned by one of the other subscriptions … for example you might want user-data of the last user to edit a post, but you can’t do this.subscribe(“post_revisions”) and this.subscribe(“ownerData”, user_ID_embedded_in_the_post_revisions) in onCreated because you only get the user’s ID once the post_revisions subscribe is ready
    • this is probably best solved with a new subscribing to a new publish. In the publish you would do a find on the revisions collection, then use the Id to do a find on the users collection, then return both cursors from the publication. Subscribing to this publish will get you all the data you need for this template.

I’m sure a bunch of these are stupid questions or have readily available answers so apologies for that.

5 Likes

check out mergebox for some answers. I believe Meteor.subscribe efficiently manages your subscriptions so the server doesnt send duplicate information.

Thanks for the link and sub-cache for that matter! I added it to the useful reading section, and threw in the answers I think it provides.

haha no problem.

What does this mean?

Not sure about ready handlers??

Flow-router’s register subscription functionality provides the functionality to check subscription readiness via FlowRouter.subsReady() within an autorun. How would you replicate this without the use of flow-router?

When you call sub = Meteor.subscribe(...), then sub.ready() is reactive

Also, given an array of subscriptions, you can wrap them in an autorun calling sub.ready() on each and AND’ing them all together to get allSubsReady
https://github.com/ccorcos/meteor-subs-cache/blob/master/src/subsCache.coffee#L119

borrowed from @mattkrick if two pages currently use the same subscription, this new template-level pattern means I would sub onCreated, stop onDestroyed, then sub again on the new page’s onCreated. This makes me wonder, would it be more efficient to just continue using a Meteor.subscribe if a subscription is used across multiple templates?

You can test this. Create a global autorun that logs the number of posts (if you’re subscribing to posts) on the client Posts.find().count(). See if you see that number change at all.

I believe if you unsubscribe and subscribe within the same flush cycle, then meteor intelligently manages the subscriptions. If you wanted to play it save you could use subs-cache which would definitely work. Just wrap it in a template autorun so it will be stopped after the template is destroyed

if you do a this.subscribe() in an onCreated and it returns ready, and then later a new document is added to the collection returned by that subscription how does the “readiness” of that subscription change? Does it always return ready? is ready re-sent when the publish data changes? if so how can you catch that?

In the docs, you’ll notice that ready is initially false and goes to true when the initial bulk data transfer is done. After that the subscription is always ready. When new data comes in, you can catch it with Cursor.observeChanges.

If we subscribe twice to identical collection data mergebox will ensure the data isn’t sent twice. But what about the behavior of the onReady callback of and the ready property of the second subscription. Are those fired immediately? I’m assuming they are but not sure.

I believe the second subscription will immediately be ready. But you’ve got to test this out yourself man! Make a little project on meteorpad.com

yeah, I’ll be digging into and answering these over time. Wanted first to see what other questions people might have, which questions already have known answers, and then dig into the most impactful ones that don’t.

Here’s what I’m doing with pubs/subs (namely showing the power of async/await):

/* server/modules/publications/Organizations.js */

import Organizations from '../../../modules/collections/Organizations'

Meteor.publish('Organizations', function() {
    return Organizations.find()
})
/* client/modules/subscriptions/Organizations.js */

let subscriptionPromise = new Promise((resolve, reject) => {
    let sub = Meteor.subscribe('Organizations')
    Tracker.autorun(computation => {
        if (sub.ready()) {
            computation.stop()
            resolve(sub)
        }
    })
})

export async function organizationSubscription() {
    return await subscriptionPromise
}
/* client/modules/some/other/file.js */

import Organizations from '../../../../modules/collections/Organizations'
import {organizationSubscription} from '../../subscriptions/Organizations'

async function someFunction() {
    await organizationSubscription() // wait for the subscription to be ready.
    let org = Organizations.findOne(/* ... */).fetch()
    // ...
}

someFunction()

I like this much better than sharing global Session variables. In case the ref to the subscription is actually needed, we can just do

let sub = await organizationSubscription()
sub.stop()

You could wait for multiple subscriptions in parallel too:

async function doSomething() {
    await Promise.all([
        oneSubscription(),
        otherSubscription(),
        anotherSubscription(),
    ])

    // subs are ready...
}

doSomething()
1 Like