Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AppConfig.ready() is Running Twice on Django Setup (Using Heroku)

I have a function that needs to run in the background on one of my web applications.

I implemented a custom AppConfig as shown below:

class MyAppConfig(AppConfig):
    run_already = False

    def ready(self):
        from .tasks import update_products
        if "manage.py" not in sys.argv and not self.run_already:
            self.run_already = True
            update_products()

However, this command is being executed twice (the update_products() call)

As stated in the documentation:

In the usual initialization process, the ready method is only called once by Django. But in some corner cases, particularly in tests which are fiddling with installed applications, ready might be called more than once. In that case, either write idempotent methods, or put a flag on your AppConfig classes to prevent re-running code which should be executed exactly one time.

I feel like I am following what the documentation says to do. What gives?

like image 746
dmcmulle Avatar asked Apr 23 '17 22:04

dmcmulle


3 Answers

As stated on this answer, if you're running your app, using the python manage.py runserver command on Django, your application will run twice: One time to validate your models, and the other one to run your app.

You can change this passing the option --noreload to the runserver command.

like image 194
Benjy Malca Avatar answered Nov 11 '22 09:11

Benjy Malca


On heroku, gunicorn is started with more than one gunicorn worker. Set the WEB_CONCURRENCY to 1:

heroku config:set WEB_CONCURRENCY=1

(see Basic configuration)

like image 31
Renzo Filini Avatar answered Nov 11 '22 07:11

Renzo Filini


Figured out that AppConfig is triggered twice and that causes the Scheduler to get initialized twice with this kind of a setup. Instead, instantiate the scheduler in url.py like this-

urlpatterns = [
    path('api/v1/', include(router.urls)),
    path('api/v1/login/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/v1/login/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
    path('api/v1/', include('rest_registration.api.urls'))
]

scheduler = BackgroundScheduler()
scheduler.add_job(task.run, trigger='cron', hour=settings.TASK_RUNNER_HOURS, minute=settings.TASK_RUNNER_MINUTES, max_instances=1)
scheduler.start()

This way the scheduler only instantiates once. Problem fixed.

like image 26
Rajesh Panda Avatar answered Nov 11 '22 09:11

Rajesh Panda