0

I am trying to roll out my own SMS verification system for my app. I don’t want to start paying for a service and then have them jack up the price on me (Urban Airship did that to me for push notification: lesson learned). During development and beta testing I have been using Twilio with a very basic setup: 1 phone number. It worked well for over a year, but right now for whatever reason the messages aren’t always delivered. In any case I need to create a better system for production. So I have the following specs in mind:

  1. 600 delivered SMS per minute
  2. zero misses
  3. save money

Right now my Twilio phone number can send one SMS per second; which means the best I can handle is 60 happy users per minute. So how do I get 600 happy users per minute?

So the obvious solution is to use 10 phone numbers. But how would I implement the system? My server is App Engine, DataStore, Java. So say I purchase 10 phone numbers from Twilio (fewer would of course be better). How do I implement the array so that it can handle concurrent calls from users? Will the following be sufficient?

public static final String[] phoneBank = {“1234567890”,”2345678901”,”3456789012”,”4567890123”,…}; 
public static volatile nextIndex;

public void sendSMSUsingTwilio(String message, String userPhone){
  nextIndex = (nextIndex+1)%phoneBank.length;
  String toPhone = phoneBank[nextIndex];

  // boilerplate for sending sms with twilio goes here
  //…
}

Now imagine 1000 users calling this function at the very same time. Would nextIndex run from 0,1,2…9,0,1…9,0,… successively until all requests are sent?

So really this is a concurrency problem. How will this concurrency issue work on Java AppEngine? Will there be interleaving? bottlenecking? I want this to be fast on a low budget: At least 600 per minute. So I definitely don’t want synchronization in the code itself to waste precious time. So how do I best synchronize calls to increment nextIndex so that the phone numbers are each called equally and in a periodic fashion? Again, this is for Google App Engine.

Katedral Pillon
  • 14,534
  • 25
  • 99
  • 199
  • 1
    Could you instead use some sort of hashing algorithm (e.g. based on user ID) to pick a number from the bank? – tx802 Mar 25 '15 at 14:13
  • @tx802 I thought this would be simplest in that it guarantees even distribution, whereas the hashing you are suggesting might not. But I can see how hashing obviates the need for concurrency. Thanks. This is a good suggestion that I have not considered. – Katedral Pillon Mar 25 '15 at 14:17

1 Answers1

2

You need to use Task API. Every message is a new task, and you can assign phone numbers using round-robin or random assignments. As a task is completed, App Engine will automatically pull and execute the next task. You can configure the desired throughput rate (for example, 10 per second), and App Engine will manage the required capacity for you.

You can try to implement something similar on your own, but it's much more difficult than you think - you have to handle concurrency, retries, instance shutdowns, memory limits, etc. Task API does all of that for you.

Andrei Volgin
  • 40,755
  • 6
  • 49
  • 58
  • My entire Twilio implementation with Tasks takes like 10 lines of code. And I don't have to worry about concurrency, retries, contention, overflows, etc. Sometimes what looks simple is not simple at all when you consider all scenarios. – Andrei Volgin Mar 25 '15 at 15:14
  • What retries? I thought Twilio queue things on their end? Are you saying the call to Twilio itself may fail? This is interesting. I was just going to use a least significant digit in timestamp as my array index. But if retries are necessary, that just adds a layer of complication. – Katedral Pillon Mar 25 '15 at 16:04
  • Retries are needed at least on services outages on either side – Zig Mandel Mar 25 '15 at 16:48
  • 1
    Also this answer is the correct way but if you must know how to atomically increment a number across several instances see the memcached api which has precisely that (But its not guaranteed to stay cached so you need a secondary mechanism) – Zig Mandel Mar 25 '15 at 16:50
  • Sorry for targeting you, I need some help with `queue.xml` and I am not sure if it makes a good stand alone question. How did you configure your push queue for Twilio verification? I have the following but I am not sure about it: ` sms-verification 200/s 100 10 `. My criteria is that I never want to create expensive instances just to contact twilio. – Katedral Pillon Mar 25 '15 at 21:53
  • Actually I have created a new question: http://stackoverflow.com/questions/29267343/configure-app-engine-push-task-queue-for-twilio-sms-verification – Katedral Pillon Mar 25 '15 at 21:59