0

I am facing a weird issue with celery in production. Currently, the production server has 4 celery workers which handle all the tasks registered by my django app. No custom queues are defined. The workers are basically 4 separate supervisor conf files.

Now, in my app I am handling facebook webhook data, and I want a user with a specific FacebookID to be only created once on my backend. But, recently I checked and found out that there are users who have the same FacebookID, which should not have happened.

What happened I think was e.g. user with FacebookID 666 sent me a webhook data. Now, a task is created which will create a new user instance in my database with FacebookID 666. Now, before the user is created in my database, the user hits me with another data, which also created a task but under a different worker, and thus I got myself two users with the same FacebookID.

Is there any way I can configure celery to handle a user with a specific FacebookID to create tasks only in ONE worker? Or have I completely misjudged the situation over here?

Mehran
  • 1,264
  • 10
  • 27
  • Disable the register button on click so that the user doesn't get to click it twice, as well as other general methods to avoid duplicate form submission (unique ids in hidden inputs etc.) - https://stackoverflow.com/questions/16814157/how-to-prevent-users-from-submitting-a-form-twice – Wiggy A. Jan 01 '19 at 10:55
  • What are you using for your broker? – 2ps Jan 02 '19 at 02:38
  • @WiggyA. this is a facebook webhook, everytime a user messages on the page this data will hit my server – Mehran Jan 02 '19 at 05:38
  • @2ps rabbitmq broker – Mehran Jan 02 '19 at 05:38
  • what about setting a `your_facebook_id_keeper_field` to `unique=True` in your database? – Chiefir Jan 02 '19 at 15:06
  • @Chiefir in that case for the first webhook hit the facebook data will be processed, but the second will throw an error – Mehran Jan 02 '19 at 17:44
  • Simply add try-except statement – Chiefir Jan 02 '19 at 17:53

1 Answers1

0

Essentially, you need a user-level distributed lock to prevent multiple workers from working on the same user. There are several ways to accomplish this, the most straightforward being a database such as mysql or redis. In mysql, the first process would transactionally (1) check for an existing row in a database table with the user ID (e.g., email or other unique identifier) and (2) if no row exists, creating that row; (3) and if a row exists, return early without doing anything. You can also do this in redis using a redlock or for smaller systems just using SETNX

2ps
  • 15,099
  • 2
  • 27
  • 47