Methods triggering more than once?

Experiencing an issue that I’ve never seen before, wondering if anyone has an idea why this is happening.

On our software, I was triggering a method for an import function. On the client side, a loading screen is shown, method call is made, awaiting the callback function to remove the loading screen. Example:

Loading_Show();

        Meteor.call('import_dailyList', data,
            function (error, result) {
                if (!handleErrors(error, result)) {
                    Loading_Hide();
                }
            }
        );

On the server side, it seems this function was triggered about 8x?

Considering only 1 person has access to this page and the client has the load screen, there’s no server side handling to prevent this from being triggered more than once, so it caused an immense amount of server lag.

Asking some other employees, they say they’ve noted similar things when the system is under heavy load.

Any ideas as to why click functions that trigger a method might be executed more than once during heavy load?

It would be great to see more details of the surrounding component, including, but not limited to, what the functions Loading_Show, Loading_Hide and handleErrors exactly do, and how exactly the triggering works.

I didn’t include those as they aren’t too relevant.

As Loading_Show simply displays a loading gif and a full screen image on a higher z-index to prevent input.

handleErrors displays an error popup in the interface, and also removes any potential loading gifs.

The trigger is using ViewModel for Blaze, a simple click binding:

<button {{b "click: importDailyOrder"}}>Import</button>

The code is honestly quite basic/standard. The only thing that should be triggering this function is the click function. The button itself was only hit once. Loading_ function and handleErrors are only used for displaying HTML on top of the current interface.

Nothing is reloading the existing interface either.

This code has been in the app for many months and this type of issue hadn’t ever happened before.

The only thing I could guess is, since we’re hosted on Galaxy, in the past if we had a function running and the server were to crash, when the server came back up, it would run the method again.

The server didn’t actually go down in this case, but I’m wondering if since the method didn’t finish, Meteor might be automatically running it again? Does anyone know the specifics of when Meteor/Galaxy attempts to re-run a method?

From the Meteor documentation: Method retries

If you call a Method from the client, and the user’s Internet connection disconnects before the result is received, Meteor assumes that the Method didn’t actually run. When the connection is re-established, the Method call will be sent again. This means that, in certain situations, Methods can be sent more than once. This should only happen very rarely, but in the case where an extra Method call could have negative consequences it is worth putting in extra effort to ensure that Methods are idempotent - that is, calling them multiple times doesn’t result in additional changes to the database.

Many Method operations are idempotent by default. Inserts will throw an error if they happen twice because the generated ID will conflict. Removes on collections won’t do anything the second time, and most update operators like $set will have the same result if run again. The only places you need to worry about code running twice are MongoDB update operators that stack, like $inc and $push , and calls to external APIs.

Can this explain somehow the phenomenon? It says “Methods can be sent more than once” – well, 8 times is more than just once, but seems a little odd.

Long-running methods can trigger this retry behaviour : Meteor call() timeout

We have this issue since we started using Meteor some 5 years ago. The easy way around is to send a random number as the last parameter (Math.random()) and then check in the method is that doc (with that random number) does exist already, otherwise insert the doc with the random number.

'kitTotalCm': function (kitUser, kit, randomId) {
        check(kitUser, checkKitUser);
        check(kit, checkKitUser);
        check(randomId, Number);

        // checks for totalCm > 0
        const methodCallsCursor = MethodCalls.findOne({ call: randomId });
        if (methodCallsCursor === undefined) {
            MethodCalls.insert({ call: randomId, created: new Date() });
            try {
                MethodCalls.insert({ call: randomId, created: new Date() });

Please excuse the wrong variable name (methodCallsCursor), it’s really ancient code.