Meteor should provide subscription caching tools


#1

Hello there,

I’ve been a Meteor user for a year now, but this is the first time i come to post here.

Subscription caching is now a must-have for high-performance applications. It’s quite critical for performances to avoid sending again and again the same data to the client as he browses the website.

Atmosphere provides 2 packages for the task, but support has been dropped from maintainers:


Thoses packages have seen their support dropped from maintainers and now suffers bugs due (mostly) to Meteor evolution.
This leaves Meteor users in an unhealthy situation, either having to develop their own packages, maintain one of those, or stay unable to elvove at the speed of meteor versions.

I believe this is the right time to start discussing about whether or not it should be Meteor’s responsability to provide subscription caching tools that evolves with Meteor.

Please share what’s on your mind.


#2

Very good topic!

class SubscriptionCacher {
      constructor(name, args) {
           this.name = name; this.args = args;
           this.shouldCancel = false;
           this.run();
      }
      subscribe() { 
          if (!this.handle) {
               this.handle = Meteor.subscribe(this.name, this.args)
          }
      }
      stop() { 
         if (!this.handle) return;

          this.handle.stop(); 
          this.handle = null;
      }
      run() {
           setInterval(() => {
                 if (this.shouldCancel) this.stop();
           }, 2000)
      }
}

Here’s your subscription cacher. Written by hand not tested just to have it as a POC.

It’s something super opinionated, but super easy to implement in my opinion. Play with shouldCancel while you navigate. It’s a bad idea to keep subscriptions from previous pages active, however you can fine-tune it however you wish.


#3

Thanks @diaconutheodor for your sample. If i understand correctly, as long as you keep the handle, the subscriptions will be in memory, right?

Well, it seems easy indeed, but i don’t think that’s the point here.
It may be a bad idea, but assuming you handled your subscription well and kept sending the minimal amount of that, keeping subscriptions active between “pages” would improve the user experience in the following cases:
A) user go to route A, then to route B, then back to route A
B) user go to route A, then to route B which use the same subs as A

On my applications, installing a subscription manager made the website lightning fast and a lot more confortable for the users.
But i also know that the exemple doesn’t make the rule.

What i had in mind for this topic was more about discussing the very idea of subscription cache, what it should be like, if there are scenarios that we (meteor users) have in common. If it appears that in-meteor subscription cache API would favor 80% of us, then i believe it’s a good change.

However, i did not expect to find a developer that is not in favor of caching subscriptions, so i find your opinion interesting. Could you develop on why you think it’s a bad idea to keep subscriptions between pages active?


#4

Perhaps you would like to help maintain some subscription caching tools if your app relies on them?


#5

To be honest, i would already have, but that coffeescript held me back :smiley:

I’m thinking more about maintaining my own, but i’m not sure about sharing it. I don’t want people to commit into using the package if I’m not sure i can allow the ressources the maintain it in the long run. Migrating out of a abandonned package may be a heavy task and it happened to me twice before. All thoses kind of “middleware” packages (routers, subscriptions, etc) may be subject to breaking changes in Meteor, and it may be hard to keep up with the speed of Meteor evolution.

I’m not trying to land extra work to the Meteor team here, nor trying to solve my issue. I simply started the discussion here to try to assess if this would be a valuable addition to Meteor or not, depending of the number of users that relies/should rely/will rely on this.

My opinion right now is that caching is pretty unavoidable if you try to keep up with a growing usage on your application. It’s about the first layer of optimization you would have to perform when your app is slowing down. As such, (and that why i started this thread), i believe it should be delivered with Meteor to maintain a working state accross deliveries.

I think Meteor is really evolving in the good direction, making things simpler to handle, and the community does provide awesome support for it. But when it comes to core questions like performances, i don’t think it’s wise to rely 100% on the community to allow simple performance optimizations through packages.

That beeing said, no one has agreed with me yet and i also may be wrong about the whole thing. What’s your opinion, @sashko?


#6

I agree, caching subscriptions (or publications?) is essential!

Yet it is interesting that nobody is talking about this or that there isn’t a common solution. And it is sad to read that development on those 2 packages mentioned in the initial post has been dropped. I’ve used these packages, they have bugs, but they kind of work.

(This makes you wonder if Meteor is actually being used in apps that have such user counts that sub caching becomes a thing)


#7

Caching is one thing, but another one is client-side optimistic UI of subscriptions. I did a PR for it #7889 - it’s not (and it won’t) be merged, but we are using it in few projects and it’s a good base for a subscription manager/cacher/whatever.


#8

Maybe I’m totally off the mark, but it’s always been my impression that many Meteor apps just load their data set client-side once, and then don’t really worry about things like pagination and caching.

I think for larger-scale apps, something like Apollo is probably a better solution to these issues.


#9

Pagination is good, but caching within pagination is (very often) designed backwards. If you take a look at most of the image viewers (native ones, not web apps), then you’ll see, that they store about 1~3 images (or rows) backwards and prefetch few images (or rows) ahead, so user don’t have to wait.

Sometimes this kind of prefetch is the best solution. For example: when client enters one view, then load only needed data (so they don’t have tot wait so long), then subscribe data for the most common views, like pages accessible from navigation. You don’t have to cache it by yourself, but if you have deterministic subscriptions arguments, then optimistic UI does its job very well - every subscription is only checked, not resubscribed (it can be checked on the client side - that’s my proposed PR job).


#10

I hope you are off the mark! :smiley: How can someone do such a thing to the poor Meteor servers ?


#11

What does it really do ? Someone in the PR said clearly, you can wrap Meteor.subscribe in a separate package, why modify all Meteor code ?


#12

@skimlab @satya guys I just showed you a PoC of a subscription cacher, done in 1 minute. You can invest at least 1 hour in it and make it behave however you want, it’s really no sad because it’s something very easy to do.

Plus, especially when you say it needs to hook into the route change. Well I may have 5 subscriptions on a page. I can have 5 different routers that are used out there.

Problem is stop relying on packages to do your work, this is a very custom work, we are talking about caching and performance optimization, this should be fine-tuned to your app needs. In my example, I showed you how easy it is, to make the subscription deactivate itself it the user left a page for more than X seconds. If he goes on another page, then comes back quickly he will see it.

Come on guys, fine-tune it to suit your needs :slight_smile: . I personally optimize pages, by first doing a method call, then applying reactivity. For example, we had a page where we had infinite scroll… and we wanted everything reactive. We ended up hooking into window.scroll function and only subscribe to elements that are visible on the page. Even if the page had 10,000 items, you were only subscribed to 10-20.


#13

Even if you have small dataset, with template / component based subscriptions, every time your route changes, you reload the template or component, and the subscription runs again.

I think a lot of Meteor devs don’t realise this is happening and that this is causing overhead on the server. So imo this should at least be mentioned in the tutorials / documentation, so that devs are aware that the ‘Meteor Magic’ has overhead.

And as Meteor wants to provide an out-of-the-box solution, why not provide a simple subscription cacher? I mean, we get accounts, we get a rate limiter, why not some caching to improve performance?

@diaconutheodor I really like your idea, will definitely give it a try.


#14

I agree subscriptions caching would be great but given that the Meteor team is currently busy building a completely different pub/sub solution (and has also never talked about subscription caching being on the roadmap) I wouldn’t get my hopes up.

Maybe you can look at Grapher instead?


#15

I developed my own subscription cache manager today (based @diaconutheodor’s sample, ccorcos:subs-cache and kadirahq:subsmanager). I wanted minimal stuff like cache limit and expiration, so it was still harder than the sample.

Basically, the most limiting area is that (like in thoses packages mentionned above) i only have control over the number of subscriptions, but not the size of the subscribed data. When setting up a cache, i would like to be able (ideally) to decide how much RAM i should use on the client’s browser for the cached data, and manage subscriptions accordingly (i.e: deleting the older ones).

I’m still thinking that ideally, the basic developer’s work should’nt be more complicated than something like:

if( Meteor.isClient() ) {
  Meteor.setCacheSize( 20 ); // 20MB
}

We could default this to 0, if we want to be careful.

Going in further for better perf tweaking, we could be defining multiples caches for managing different lifetimes with a slightly different API:

// cache for admin stuff, 40MB size, 5min lifetime
var adminCache = new Meteor.Cache(40, { expire: 5 }); 
var subHandle = adminCache.subscribe(...);

Destroying the adminCache object here would result in a cleaning of the cache and subscriptions (provided they are not also subscribed by other Meteor.Cache objects).

I hear that some of you think that performance optimization should be fine tuning. That’s right, but only at some stage of your project. Let’s no be blind here.
I’ve used Meteor for several projects already, and installing a subcache package took me a few minutes and resulted in a drastic performance improvement for almost all uses case on all my apps.

Meteor already did a great job reducing the client/server communication into a simple pub/sub/templating pattern, so why not go further and also cut the bandwith usage with a simple caching tool?


#16

And what if not even one subscription will fit in your cache?


#17

But then, with the cache, what you are left of is just a “presumption” of real time, what if the subscription changes ? Keep in mind that every publication is kept on server as well! Unless you are actually listening to the changes… you could simply just do a Method.call, and invalidate it every 5 minutes :smiley:


#18

@radekmie Then we don’t cache anything and the situation is not any worse. But if that happens, well, i guess you have bigger issues than cache. Subscriptions should be designed to be (ideally) as small as possible, sending the minimal amount of data for the page/template/whatever to load. Sending several MB of data for a template seems too much, but i may miss some use cases here.

@diaconutheodor From my tests today, it seems that keeping the handle alive is enough for the subscription to update when data changes (but i’m not sure anymore that wasn’t just the optimistic UI). When you load a template and perform a server-only Meteor call, does the UI refresh accordingly to server data changes? Anyway, that should not be a problem from Meteor core dev (since they can access the whole Meteor middleware, they can exchange data on the websocket if refresh is needed).