Help with double server method calls

Meteor version: 1.8.3
Node version: 8.11.4
Apps are docker containers hosted on a linux machine on AWS.

I’m having a production issue where an API call (made from Braintree hosted fields’ onSubmit()) is running twice, but only about 5-10% of the time. I have not once been able to replicate this issue locally or even on our dev environments. The dev environments are identical aside from the amount of users on it (we normally have 50+ logged in but significantly less are using this payment form). This code has been working normally for years and just started acting up about 2 weeks ago.

Things I have tried:

  • ensured client cannot spam the form submit
  • event.preventDefault() is the very first line of the submit function
  • DDPRateLimiter is implemented and working. the config specifies to block any call to this method if the method has been called in the last 30 seconds (per userId)
  • Changing the Meteor.call() to Meteor.apply() with noRetry: true (see below, not 100% sure I did this right)

The DDPRateLimiter fires the callback as expected (in my case it just logs that the suppression fired so I know its working), but when the 30 second interval is over, the method just fires anyway.

I’m not 100% sure I implemented Meteor.apply() correctly because I couldn’t find a single code example that uses the noRetry option. For the sake of explanation I’ll write a fake Meteor.call method here that I’m trying to convert to a Meteor.apply method with a noRetry: true option. I can’t really post the code as it is owned by my company.

Original code:

Meteor.call('processPayment', customerName, balance, function(err, res){
	if(res.success){
		// show success dialog
	} else {
		throw new Meteor.error()
	}
})

New code:

Meteor.apply('processPayment', [customerName, balance], true, function(err, res){
	if(res.success){
		// show success dialog
	} else {
		throw new Meteor.error()
	}
})

The issue persisted after this change so I’m assuming I’m doing something wrong because it definitely seems to be retrying the method call as soon as the DDPRateLimiter allows it to.

Based on old StackOverflow posts, this was the correct way to call Meteor.apply() with the option noRetry: true. However when I visited the docs here: https://docs.meteor.com/api/methods.html#Meteor-apply it looks like you’re supposed to pass it as something like {noRetry: true} instead of just true:

Meteor.apply('processPayment', [customerName, balance], {noRetry: true}, function(err, res){
	if(res.success){
		// show success dialog
	} else {
		throw new Meteor.error()
	}
})

My IDE doesn’t like the fact that I pass {noRetry: true} (gives me a warning) but the method does still run as expected.

Final Notes:

  • Again this code has been working for years without issue (no DDPRateLimiter or anything). Even when the first call returns a successful response, a second method invocation will SOMETIMES go out.
  • I’m trying to put a bandaid on the issue until I can find time to re-write the entirety of the process, although it seems like Meteor has an overwhelming amount of ways to write async code/promises.
  • The users submitting this form are using Chrome on a Desktop PC, internet connectivity is not an issue.
  • About a week before the first instance of this issue happening we migrated all five of our servers to an AWS Load Balancer (handled by our devops team). Devops insists that this isn’t the reason but if that sounds like a clue I’d love to hear thoughts on it.

Appreciate any help yall can offer, let me know what other information I can offer to help debug.

Thanks,
Ed

It is possible that the event is being triggered multiple times on the client. Try debouncing the form submit event.