Reputation:
I have recently moved my Symfony Project to Elastic Beanstalk, from a manually scaling EC2 environment, and i have stumbled on to an issue.
I have a Cron job that grabs a bunch of subscriptions that are past due, and attempts to create orders and charge them, it is very important that this job only runs on a single server cause it grabs them in a batch, if there is a duplicate Cron running, it will lead to multiple charges on the same subscriptions.
In the manually scaling environment, I just had a primary server, that had a crontab and ran the subs, but that does not seem doable here.
Any suggestions on how to set this up in a auto-scaling enviroment with no primary server?
I have looked into JMSJobQueueBundle, but it also seems to rely on supervisor only running a single instance of the crontab, this would be duplicate in an auto scaling environment.
Is my best bet to have an external server ping my API every X minutes and launch the job on the single instance it pings? This seems to introduce another point of failure.
Upvotes: 2
Views: 903
Reputation: 4292
Just as a warning, the problem you are trying to solve is not a trivial one.
A few options:
If you connect to a MySQL database, acquire a lock (https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock) at the beginning of the transaction and release the lock at the end. this will prevent multiple cronjobs to run the code at the same time. works only if everything is handled inside SQL.
As you mentioned, you can use AWS Lambda + CloudWatch Events to trigger a Lambda function every x minutes and the Lambda can than trigger your script via http(s). But lambda does not guarantee that is does so exactly once, so this is not safe.
Use a service where you can acquire a distributed lock (e.g. etcd, consul, redis, ...) and implement in a similar way than 1 just not in your MySQL database. Still you can nor really enfore exactly once with this.
put the cronjob on a separate ec2 instance where you know that it exsists only once (in case of this ec2 instance goes down nothing will run, but as far as I understand your requirements this is not a problem because the script can run 15 minutes later and still catches up with all the work.
The more "modern" approach in distributed systems would be to make your action idempotent.
Upvotes: 3