Implemented it using mixins with validated methods
There is a difference. You can use this.unblock()
on specific lines of code within your method. Can be useful for optimistic UI implementation for example.
I just wanna give a slightly different perspective:
→ We don’t use this.unblock, almost never.
As I understand it, and please correct me if i’m wrong, this only unblocks multiple methods being called by the same user.
As we’re using a “classic” “methods for UI Interactions / Update Requests to the server” / Pub/Sub for the dataflow from the server to the client (Using blaze btw) model this is almost never a thing we’d actually need and would be more of an issue as it’s more “thread-safe” per user to not having multiple methods running in parallel.
Would consider it for long-running methods of course.
But I don’t use it as a general performance booster or somesuch and it wouldn’t do a lot for us.
Multiple requests / method calls by multiple users in parallel are already paralellized by meteor / fibers.
And not having unblocked code gives nice ordering / predictability from the client side as well.
So there you go! Don’t just unblock everything - or do - but know what you’re doing! It might not do a whole lot depending on the use case.
I think it’s important to understand what it’s doing and what it’s for.
We use it by default for all declared Meteor methods + publications. This excludes the Meteor methods automatically declared for client-side db updates. It’s almost never been an issue for us.
I think there is some misconception here (quote from the original topic):
I really don’t know how the above code could have the same effect as running this.unblock()
(e.g. in Meteor 2.3 or with an external package). This code merely delays the setup of the respective publication, not its execution. @robfallows also hints that when he writes…
[…] if you have many publications and they take a long time to set up, you can set them up asynchronously
The callback of the publication will first be invoked when a client calls Meteor.subscribe
, but then the invocation of Meteor.setTimeout
won’t play a role anymore.
So unless I’m mistaken, the above “trick” with Meteor.setTimeout
isn’t a substitute for this.unblock()
in publications.
Using unblock in methods seems to work fine, but be cautious with using it in publications. I tried this and realized that it causes Meteor to invalidate and resend all publication data every time you change the input parameters. Without unblock, it will only send the deltas. For my geospatial app, this caused a huge performance degradation. Your mileage might vary.
More details here:
@waldgeist to clarify, when you tested this.unblock() within a publication back in February, were you using the Atmosphere package meteorhacks:unblock or lamhieu:unblock?
I am using lamhieu:unblock
. I try to avoid the meteorhacks
stuff, as it is unmaintained. Arunoda left the community long time ago.
BTW: You can easily verify my statements using Meteor Dev Tools. Subscribe to a publication with a lot of documents and then change the input parameters, so only a fraction of the documents would have to be deleted or added. Without unblock
, you will only receive the deltas. With unblock
, all documents will be deleted and re-added afterwards. Which can cause lots of unnecessary data transfer and waiting time. In my app (which is geospatial), it made the map display unusable.
Unfortunately, I only noticed that after a while, so it was very hard to track this down to its actual root cause. I only noticed it because in our Augmented Reality frontend content vanished and popped up again without any reason, which did not happen before. This was caused by the deletions and additions in quick succession.
I am wondering if the new built-in unblock
feature in Meteor 2.3 has the same problems?
Very interesting thread!
I’m also aligned with @DanielDornhardt’s comment. I’ve almost never used this.unblock.
There was one specific time where I had to retrieve information from an external database in three different components rendering in parallel. So each component had a method that queried the external db and brought some info, right?. In this case it was pretty evident that things where happening sequentially, extending the overall time needed for the page to be completely ready.
Unblocking these methods made the calls in parallel and the experience was much better (reducing the overall load time to around 1/3 of the blocked/sequential scenario).
I’ve never needed (or thought of) unblocking pubs (I don’t quite understand the pub “setup” hack suggested by Rob) or unblocking methods for any other case in an enterprise app with more than 2 years of development.
I’ll keep an eye on this thread to learn more on the subject (and dive-in in the suggested links)
Thanks!
Regards
In React, with components and state handling, I do not see much need for executing methods that are not in parallel. Imagine loading a modular page with different components each with different method calls.
If you need a method dependent on another method, you can either
- call the method inside the other method in the server
- use the callback of the method in the client
In vanilla JS, either modern or not modern, when we invoke anything that has a callback, we comprehend that it is unpredictable, when it will actually be called.
When we make multiple calls right after each other, each of which has its own callback, we do know that the order in which the individual callbacks will be called is unpredictable. So far no one is surprised, this is just how Javascript works. If we want those to be executed in a particular order, there are multiple simple ways to manage it, for example:
- the dependent invocation will be made within a callback of the call on which it depends
- or we can wrap each invocation (with a callback) into a Promise, so we can chain the invocations using
Promise.then()
Meteor introduced a sequential method execution order, which is very unusual in Javascript. The creators shouldn’t have done that, I think it was a mistake. This mistake will be exacerbated by this.unblock()
which can be used selectively. This way it becomes totally opaque how a sequence of Meteor method calls will be processed. For example:
Meteor.call('methodOne', methodOneParams, methodOneCallback);
Meteor.call('methodTwo', methodTwoParams, methodTwoCallback);
How do you know whether methodTwo
will be invoked strictly after methodOne
?
The answer is that you can’t possibly know just by assessing the client code. Your only chance to find out is to actually go look methodOne
's code and check if this.unblock()
is invoked. That’s totally crazy, and leads inevitably to misunderstandings, and that may lead to bugs.
Everyone has interesting views / experience on this I’m finding it really helpful. I can see that maybe there are two app architectures approaches emerging. I’ll have to get around to trying both approaches to see what performance differences emerge for me.
I’m seeing:
Approach 1: Use this.unblock rarely (prioritize server performance)
- Use for known long running methods, usually to 3rd party APIs with unknown performance
- Order of methods & subs is thoughtfully designed on the client
- Usually not running all methods & subs in one batch, on page load for example
- Methods & subs running serially would reduce the peak loads on the server, but it could create long load times on the client (to use an extreme, if 100 things run serially, a big user wait time would be present on the client)
Approach 2: Using this.unblock often (prioritize client performance)
- Use for all methods & subs because they are often running in parallel, batched per client
- Order of loading methods & subs have almost no interdependencies
- Any interdependencies are thoughtfully designed on the server / client
- Methods & subs running in parallel would probably put more of a spike load on the server per client connection (to use an extreme, if 100 things run at the same time a big performance spike would be seen on the server)
- If there is plenty of performance overhead on the server (extra $$), the end user will probably experience a very fast app, if the server limits are reached, there could be user issues experienced
- Probably prioritize bigger containers over “more” containers in production
It seems like there is the room to make choices between 1 & 2 or have a mix. This should be fun to dive into further!
If anyone also wants to share how their basic app stacks look & how their apps are running in production that would probably be helpful to go with more comments. Meteor is so flexible we are probably just seeing a couple of the most common architectures emerge.
Exactly. This is too much overhead cost which grows as the code grows and as well as the team. That’s why we choose to do one thing for everything.
I don’t buy this as a reason but as an excuse. Not sure if anybody here really avoid parallel calls for server performance
I think this whole thing is an artefact of Meteor’s creation in a time when there wasn’t a “normal” and the Meteor team bet big on Fibers and being able to write sync style code everywhere for async operations.
The only way to have simple sync execution on the client was to force those calls to be executed in a queue,
Now, I agree that going all-in on Fibers was a mistake. No other framework chose to follow the same pattern, and then the major async pain points started getting solved by language features like Promises, Generators and async/await.
But before these existed, I can understand why MDG made the choice they did.
Personally, I am fairly agnostic to the approach, but our impementation makes many inter-related db calls, and users can often make many changes to the same collection over a few seconds.
For this reason we’ve kept the blocking behaviour by avoiding async/await and promises in methods. Otherwise we would probably have issues with order of operations and inconsistent final state.
We also extensively take advantage of Meteor’s optimistic UI, so even when the server is busy processing all the instructions, the client doesn’t notice because it’s already updated for them.
Good points. this.unblock()
and sync methods are very useful for “real-time apps” without handling state manually in the client
Our use case for running this.unblock() in publications is simply to allow a web page to finish loading a bit faster. In our Meteor apps, the client subscribes to multiple publications and we want to avoid an unnecessary delay on the server side.
@vlasky does your PR for using this.unblock()
in publications for 2.3 prevent the issue experienced by @waldgeist ?
@rjdavid I expect it to experience the same issue. I quote from my previous reply earlier in the thread.