I recently did a project using Twilio that sent SMS text notifications to about 1000 users as fast as possible. I sent the messages in a loop to each user and used a Twilio Messaging Service with about 30 toll-free numbers that can each send three SMS notifications per second. Twilio recommends using multiple numbers for 1) speed - you can distribute the notifications over all the numbers and 2) sending the same notification to a lot of devices on the same number can cause the carriers to flag it as spam and deny future notifications. Using more numbers reduces the likelihood of getting flagged as spam.
The simplified code looks like this:
_.each(users, function(user, index) {
try {
TwilioClient.messages
.create({
body: notificationBody,
messagingServiceSid: process.env.TWILIO_MSG_SERVICE_SID,
to: user.phoneNumber
})
.then(function(message) {
console.log("Successfully sent SMS notification " + (index + 1) + " -", message.sid);
})
.done();
}
catch(error) {
console.log("There was an error trying to send an SMS text message. ", error.reason));
}
});
It worked well in testing on a few users, but in production with hundreds of users I kept having server crashes with the error message Too Many Requests
. After some thought and trials, I figured maybe it was creating too many requests too quickly, so I added…
Meteor._sleepForMs(250);
…just above the try-catch
and this fixed the issue and allowed the server to send out all the messages, but slower. I’m assuming this gave the server time to get responses from Twilio before piling on more requests. Without the the sleep call, I’m pretty sure it was loading up all 1000+ requests almost instantly which crashed the server.
My question and reason for posting is to ask if what I’m assuming above correct? Do you have to add some time for the requests to third-party APIs to return before firing new ones? I’m kind of surprised because it didn’t look like my server was running out of memory. Besides, 1000 3rd party API requests can’t take up that much memory? Is this more of a Node limitation? A config var that can be tweaked? Is adding a sleep like this the correct approach? Seems kind of like a low-level band-aid instead of doing something more correct or best-practice.
The goal was to get the notifications out as quickly as possible… all 1000 in under one minute if possible. With my sleep call of 250ms, it raised the time up to 4.16 minutes to get all 1000 sent. This was still acceptable, but under one minute would have been nice. I didn’t do any tests to see if I could have went with a shorter sleep time.
Also Twilio recommends using a loop like this. They don’t have an API function exposed that could send 1000 of the same message to a giant array of numbers all in one shot. You have to loop one-by-one. I wrote Twilio support about this and they confirmed.