Hi all,
building on top of my previous question:
I have an app on Heroku that has one user-triggered compute intensive task that gets sent to a background task worker via Celery/Redis.
If my understanding is correct, this should prevent overloading the web server, i.e. the app should always remain responsive. If there happen to be more users, they would have to wait longer for the results to come back, but the app should remain accessible.
However, what I observe, when load testing the system, is that after two or three jobs have been submitted to the queue the next time I try to access the app, I am served the landing page but it doesn’t initialize until the jobs are done (the tab title says “Updating…”). Even more confusing to me is that if I initialize the app several times (i.e. go to the URL and have the app wait for input), then start two or three jobs, I can still use the other previously initialized instances without any issues.
I tried upgrading the Heroku instances from the free tier to the paid for tier and increasing the number of workers for the app and the queue to two without success.
Tbh, I don’t even know how to go about troubleshooting this, so any input would be very welcome.
The basic code structure looks as follows:
if 'REDIS_URL' in os.environ:
# Use Redis & Celery if REDIS_URL set as an env variable
from celery import Celery
celery_app = Celery(__name__, broker=os.environ['REDIS_URL'], backend=os.environ['REDIS_URL'])
background_callback_manager = CeleryManager(celery_app)
else:
# Diskcache for non-production apps when developing locally
import diskcache
cache = diskcache.Cache("./cache")
background_callback_manager = DiskcacheManager(cache)
external_stylesheets = [dbc.themes.BOOTSTRAP, dbc.icons.FONT_AWESOME, dbc.icons.BOOTSTRAP]
app = Dash(__name__,
external_stylesheets=external_stylesheets,
background_callback_manager=background_callback_manager,
)
server = app.server
The callback looks as follows, also following the docs. I have a progress bar and I’m returning the progress explicitly for the user:
@app.callback(
output = Output('growth_data_auto', 'data'),
inputs = Input('auto_fit_button', 'n_clicks'),
state = list_of_states,
background = True,
running = [
(Output('progress_count', 'style'),
{'visibility': 'visible', 'justify-content': 'center'},
{'visibility': 'hidden'},
),
(Output("progress_bar", "style"),
style_progress_bar,
{"visibility": "hidden"},
),
),
],
progress = [Output('progress_bar', 'value'), Output('progress_bar', 'max'), Output('progress_count', 'children')],
cancel = [Input('cancel_auto_button', 'n_clicks')],
prevent_initial_callback = True
)
def function_of_interest(set_progress, x, y, z):
for i in range(n):
set_progress(i, i, i)
do_compute_intesive_task(x, y, z)
The procfile looks like this:
web: gunicorn app:server
queue: celery -A app.celery_app worker --concurrency=2 --loglevel=INFO
Thanks,
M