[SOLVED] This.unblock() vs async vs Meteor.defer()

As part of my performance optimization efforts I’m going through my Methods and making sure to make them asynchronous if possible.

From reading through the Galaxy APM documentation I’m a little unsure if it matters if I use this.unblock vs async vs Meteor.defer for this, and was wondering if anyone had any opinions or advice on this.

thanks!

Meteor methods are executed one after another for the same user.

For user 1, if method A is called, and then method B, the server has to wait for method A to finish before executing method B.

this.unblock() in method A means method B can run after the unblock and not wait for A to complete for user 1.

We have a general function in our projects which calls this.unblock() for all methods at the start.

Meteor.defer() makes the code async or moves the code execution to the callback queue and out of the main loop. This removes potentially blocking code from hogging the cpu of the server. In terms of “unblocking,” it unblocks the main loop for all users.


async is similar to Meteor.defer() in terms of “unblocking” the execution. Most useful when you have to await a function especially if you need the results of that function as a return value to the method which is not possible with Meteor.defer()

9 Likes

I guess my question is then, is there any reason not to just use Async everywhere when you need to run code asynchronously? I haven’t been able to see any advantage to using this.unblock or Meteor.defer over Async.

Since our methods and publications are default unblocked (this.unblock()), I cannot answer for this but it generally is not an equivalent for async and the use case is different

Between async and defer, for us, it is just semantics. When I see an async function, I immediately expect an await inside. When I see defer, I immediately know that the process inside is not urgent for the method.

Performance-wise, I don’t see any advantage

My understanding is that as long as neither this.unblock() is called nor Meteor.defer() is used, Meteor will make sure to send the DDP messages about the respective method’s finishing in the same order as the methods were called.

This is supposed to be regardless of whether or not the method is declared async. Meteor checks the type of the return value of the method, and if it is a Promise, it waits until it clears either way.

But I haven’t actually tested yet whether using async functions in methods can break the guarantee of sequential execution in Meteor. It would be unsettling if it did. Did you test this?

There’s a bit more here:

  1. Regular methods
    Method calls for the same client enter a queue and are executed in the order that they were called. Will block until fucntion returns, other method calls from the same client will wait until this is done before executing.
    Note that Meteor does some async work in a sync style using Fibers (eg. DB queries, HTTP, etc) that don’t block the main thread (so other requests can still be served) but will continue to block the queue for this user.

  2. Regular methods with this.unblock
    Calling this.unblock takes the current method out of the wait queue so that the next method from the same client can start work.

  3. Async methods
    This includes async functions and normal functions that return a promise.
    Async methods automatically call this.unblock()
    The main advantage here is that it allows async/await and/or makes working with callbacks easier.

  4. Methods with Meteor.defer
    Meteor.defer is very similar to setTimeout. Like setTimeout the call returns immediately, so a method call is likely to return immediately. This means you can’t return a result from the part of the method in defer.
    If you wrapped your defer in a promise so you can return a value from inside defer, you end up with effectively an async method and the rules of 3. apply.
    However, if you wrap your defer in a Meteor.wrapAsync in order to return a value, because it’s running sync style the rules of 1. apply and methods will have to wait in the queue

6 Likes

It is new to me. While I don’t doubt what you were stating is true, could you point me to the source of this? Is it documented somewhere, or did you find out about this by consulting Meteor’s core code?

1 Like

This one I also something new to me :grin::+1:

Even with defer, the order of calling the methods will still be followed. (Unless returned as a promise as mentioned for async above)

1 Like

Is that really so? That’s quite shocking. The docs say

Defer execution of a function to run asynchronously in the background (similar to Meteor.setTimeout(func, 0) .

Furthermore, there is a comment in the code prepending the function definition of Meteor.setTimeout that says:

Meteor.setTimeout and Meteor.setInterval callbacks scheduled
inside a server method are not part of the method invocation and
should clear out the CurrentMethodInvocation environment variable.

We are talking about the methods and not the function inside defer. Take note that defer can just cover a part of the method.

We have methods that require a result but some parts are not required to get the results so we normally put those under defer

I am totally aware of that distinction. In most cases I guess if someone uses Meteor.defer, this is going to be meant for the entire code of the method, and in that sense it is misleading to say that what is being executed within that context, “order of calling the methods will still be followed”.

While the method itself will be invoked in the order as the call was made by the client, the actual code wrapped into Meteor.defer will be executed outside of that order.

I couldn’t find any mention of this in the docs, but this blog post by @robfallows goes over everything in detail:

https://blog.meteor.com/fun-with-meteor-methods-a0368ee0974c

And it has a great overview in the conclusion:

In Conclusion

  1. Methods are independent as far as different clients are concerned. So for example, Bob’s client can call method A at the same time as Carol’s client calls method A — those method invocations will run concurrently — neither Bob nor Carol will wait for the other*.
  2. Using this.unblock() to try to improve (1) will do nothing — it’s already running as efficiently as it can.
  3. For any one client
    3.1. Methods are executed in the order they are called from the client — imagine a FIFO queue on the server for each client.
    3.2. If this.unblock() is not used, methods are also evaluated in order — the method runs to completion and then the queue is popped for the next method to execute.
    3.3. If this.unblock() is used (or you’re using async methods), methods may be evaluated out of order — the queue is popped until empty, each method starting up in a new fiber as soon as it’s popped. Methods finish independently of the order in which they were initially present in the queue. This may result in unpredictable interleaving which is a recipe for hard-to-diagnose, race-induced bugs. If you need to take advantage of this approach, you also need to be certain that it’s safe if your methods complete out of order.
  4. Use that async keyword as a handy reminder that you should check for interleaving safety.
  5. In order to avoid thinking about interleaving, you should enforce it at the client level (or use neither this.unblock() , nor async methods). It’s easier to do this using async/await on the client — the alternative is nesting and callback hell.
  6. If you’re using async methods, you don’t need this.unblock() .
2 Likes

@peterfkruger just to remind you of your post that I replied to

You are the one talking about DDP messages finishing as the methods were called

We’re trying to clarify how the order of execution of our code in methods will be affected in the cases without this.unblock(), with this.unblock(), async and Meteor.defer.

It is true that using Meteor.defer will not change the order in which the corresponding method is called, but while it is true, it is at the same time also somewhat misleading.

The reason is that if the actual code of that method is wrapped in Meteor.defer, the execution of it will fall out of the initial order, and in the context of this discussion this is what counts.

1 Like

Back to the topic…

One big distinction between async, defer, and unblock is that Meteor.defer() and this.unblock() can be used to fine tune the methods. The developer can decide which part of the method to defer or where in the code to initiate unblock.

If we are using sync methods, I see a possibility of a DB process required for the next method. So just after the DB query, there is an opportunity to call this.unblock() so the next method can start. This is one thing I don’t think async meteor methods can be a substitute to

This is a good point.

Would it not provide the same effect though if we were to call await on the DB call inside the async function?

Good question. I’m not 100% sure of the effect of calling await on a sync function and how is that considered by Meteor.

If the method’s function is async, the whole method will be treated as if we issued this.unblock() as its first statement, according to robfallows investigations.

But I don’t even see the point why we would try to keep a sequential execution of Mongo queries in subsequent methods. MongoDB is highly concurrent, plus it will receive concurrent queries from method calls made by other clients, and possibly also from other Meteor instances.

Upon further reflection, I think you’re right.

The async function would pull the whole method out of the queue, and the await would only affect any code inside of that method. Not the whole method queue.

I also agree that I can’t think of any use case where I would need to wait on half of a function, and if I did, I would probably break it out into two methods.

1 Like