Very weird "bug" - Celery (Redis)

Hi, I experience a very weird bug. I deployed a FLASK web app, with as main .py files main.py, at the root of the project, with most of the code, incl. a function which triggers a celery task located in tasks.py celery_config.py (with the Celery configuration) tasks.py, where I have my celery task I have 2 services in my project - a Redis database - the Flask Web App When I trigger in my web app the action which is supposed to launch the celery task, I see an entry (celery) in Redis, which proves that the web app could communicate with Redis, but the task doesn't actually start. But then when I spin Celery in my local development instance (using the same start command as the one in my Procfile), on my computer, the task is actually triggered and it's completed. The problem is that the output file which was supposed to be generated and stored in the cloud by the web app is then stored on my local machine, since the process actually completes on my local machine, not in the web app.... In my Procfile (for the Flask web app deployment), I inserted the 2 lines which are supposed to both spin the server for the web app and the celery service. web: gunicorn main:app worker: celery -A celery_config worker --loglevel=info What am I missing? Why isn't Celery working as expected in the cloud version of my app? It works fine locally (connecting to the same Railway Redis database as a broker, via the same URL).
Solution:
i changed my mind, we'll try to do this all in a single config file, so lets see if this works.. add a railway.json to your project with this in it: ```json { "$schema": "https://schema.up.railway.app/railway.schema.json",...
Jump to solution
93 Replies
Percy
Percy4mo ago
Project ID: N/A
CallMeFred
CallMeFred4mo ago
Project ID: a7e74948-3de2-4e70-afa7-622b30e041b0
Brody
Brody4mo ago
railway does not natively support running parallel commands in a single service the same way heroku does. you instead need two railway services, one that runs gunicorn, the other that runs clery. this is achieved by having two near duplicate services in your railway project with the applicable start commands set in each service.
CallMeFred
CallMeFred4mo ago
So I need to deploy twice my web app basically? With 2 different Procfile ?
Brody
Brody4mo ago
yes but forego the procfile entirely, set the applicable start commands in the service settings
CallMeFred
CallMeFred4mo ago
here?
No description
CallMeFred
CallMeFred4mo ago
So for one version: web: gunicorn main:app And for its clone: worker: celery -A celery_config worker --loglevel=info ?
Brody
Brody4mo ago
correct
CallMeFred
CallMeFred4mo ago
What is the impact price wise of duplicating a full app?
Brody
Brody4mo ago
the full app is not duplicated in the sence you think, one service will only be running gunicorn, the other service will only run celery
CallMeFred
CallMeFred4mo ago
So no negative impact price wise?
Brody
Brody4mo ago
im sure theres some, but this is simply how its done on railway
CallMeFred
CallMeFred4mo ago
Why do you have to clone the full app? Can't you just isolate some .py files for the Celery part?
Brody
Brody4mo ago
why would that be necessary? if your start command is set to run celery than that is all that will be ran
CallMeFred
CallMeFred4mo ago
So you could have some dummy empty project just to run Celery? Not the full Flask web app?
Brody
Brody4mo ago
why would you want that? again, if your start command is set to run celery that is the only thing that will be ran on that induvial service
CallMeFred
CallMeFred4mo ago
So in that service you would just need basically what's required to spin celery (in the requirements and in the code) but not the full code of the full app since Celery isn't used in the full app, just for 2 tasks...
Brody
Brody4mo ago
again, why would you want to do that? you are trying to make this far harder on yourself development wise by breaking the repo up
CallMeFred
CallMeFred4mo ago
By the way, I haven't cloned yet the full app but the first instance of the service doesn't start anymore.
CallMeFred
CallMeFred4mo ago
No description
CallMeFred
CallMeFred4mo ago
errors...
No description
Brody
Brody4mo ago
the start command field is not in Procfile format, forego the web: part
CallMeFred
CallMeFred4mo ago
good to know... and for Celery?
Brody
Brody4mo ago
same thing
CallMeFred
CallMeFred4mo ago
so no worker: Just celery -A celery_config worker --loglevel=info
Brody
Brody4mo ago
you'd likely want to set a fixed concurrency value there, but yes, the start command field is not in Procfile format
CallMeFred
CallMeFred4mo ago
Which concurrency value do you recommend? It's a process which concatenates mp3 files, not machine learning heavy
Brody
Brody4mo ago
start with 1, you can always increase at any time
CallMeFred
CallMeFred4mo ago
celery -A celery_config worker --loglevel=info -c 1 ?
Brody
Brody4mo ago
personally, id use the long form flag
CallMeFred
CallMeFred4mo ago
celery -A celery_config worker --loglevel=info --concurrency 1 let's see if this thing will work now... I spent 4 hours trying to figure out how to make Celery work on Railway. I should have asked earlier....
Brody
Brody4mo ago
haha yes you should have you are missing the equals sign between that last flag, not sure if it matters but probably best to have it
CallMeFred
CallMeFred4mo ago
OK so... The "task" now completes. But when I try to access the file where it's supposed to be... it isn't there. I suspect that the web app triggers the Celery task, which is performed, kind of generates the expected file but it's not stored in the expected location.
Brody
Brody4mo ago
where is the file supposed to be saved to?
CallMeFred
CallMeFred4mo ago
in a folder at the root of the web app project /output (the folder exists)
Brody
Brody4mo ago
railway does not provide a native way to inspect the contents of the container, so im not sure how you are verifying that
CallMeFred
CallMeFred4mo ago
I know that the folder exists because I force the creation of folders if they don't exist when the web app starts the problem is that the file doesn't get saved... in the folder (it's all fine in local development where both services are spun from the Terminal in the same environment)
Brody
Brody4mo ago
the two services are in isolated containers
CallMeFred
CallMeFred4mo ago
So what I suspect... is that the file gets saved in the Celery app 🙂 I mean in the Celery version of the app... which can't be accessed by the actual user-facing app
Brody
Brody4mo ago
the flask service can not access the celery service's filesystem, that is correct
CallMeFred
CallMeFred4mo ago
so basically I did all of that... for nothing since my Celery task can't actually save a file available to the web app which triggered the task wonderful
Brody
Brody4mo ago
i was not aware you where doing such things i was under the impression that you saved the file to storage that was accessible to both services
CallMeFred
CallMeFred4mo ago
In the first place I didn't know I needed two cloned versions of the same app... so my logic is just based on stuff happening in one app not in version A & B spun separately for technical reasons 🙂
Brody
Brody4mo ago
two services for separate apps is standard practice on railway, developing like you are using heroku is going to bring only headaches
CallMeFred
CallMeFred4mo ago
I have never developed on Heroku... I've been a proud Railway customer for months. It's just that it's the first time I need this Celery thing
Brody
Brody4mo ago
in fact, ive heard through the grape vine that heroku regrets allowing the web and workers in the same container Procfile is a herkou thing, railway only has minor support for it
CallMeFred
CallMeFred4mo ago
OK, so basically, what I have to do is revamp my dual-head app and store the final file on S3 and communicate the S3 URL back to the download button instead of a file system location...
Brody
Brody4mo ago
i mean there are better options then s3, but if you don't want to change code then we can run two apps in the same service if you would stronly prefer not to change code
CallMeFred
CallMeFred4mo ago
in a custom way? Or is this a feature?
Brody
Brody4mo ago
you could call it a custom way, is that something you are interested in?
CallMeFred
CallMeFred4mo ago
What does it require at my end?
Brody
Brody4mo ago
two config files that id write for you
CallMeFred
CallMeFred4mo ago
OK, let me know exactly what I'm supposed to do and I'll test the result.
Brody
Brody4mo ago
this means you are going back down to one service, so delete the celery service, remove the custom start command, and delete the Procfile
CallMeFred
CallMeFred4mo ago
OK, Procfiles were removed from both. I'm deleting now the celery service.
Brody
Brody4mo ago
from both?
CallMeFred
CallMeFred4mo ago
I used the start command in both when they were separate services
Brody
Brody4mo ago
im getting the feeling you duplicated your repo?
CallMeFred
CallMeFred4mo ago
yes that's what I did one starting as web, the other one starting as celery worker one repo, two services two services created from one single repo with 2 different start commands as instructed but I can now delete the one we used to start Celery
Brody
Brody4mo ago
why did you create a duplicate github repo?
CallMeFred
CallMeFred4mo ago
I didn't create a duplicate github repo I created 2 services on Railway from one repo
Brody
Brody4mo ago
you duplicated your repo? yes that's what I did
CallMeFred
CallMeFred4mo ago
Well, sorry for the misunderstanding... I'm getting a bit tired 🙂 So shall I delete the "duplicate" on Railway published to spin Celery?
Brody
Brody4mo ago
yes
CallMeFred
CallMeFred4mo ago
done so I'm left now with one service with a gunicorn start command I'm documenting what I'm doing so that this thread can be useful for random strangers
Brody
Brody4mo ago
remove the start command, then save without deploying alt + shift + enter
CallMeFred
CallMeFred4mo ago
alt shift enter doesn't seem to save on Windows (after removing the content of the field) shall I press on the v ? ok so I pressed on the v and then I did alt shift enter which worked
Brody
Brody4mo ago
V?
CallMeFred
CallMeFred4mo ago
the little v icon next to the x icon
Brody
Brody4mo ago
im drawing a blank here, please show me the V icon
CallMeFred
CallMeFred4mo ago
No description
CallMeFred
CallMeFred4mo ago
I removed the content of the field, pressed on v and then did alt-shift-enter we're good next = the custom file which will do the magic the secret sauce
Brody
Brody4mo ago
bruh thats a check mark
CallMeFred
CallMeFred4mo ago
well yeah, which is a little "v" 🙂 anyway, we got there show me your magic now
Brody
Brody4mo ago
go to the optometrist after this 🤣
CallMeFred
CallMeFred4mo ago
I promise
Solution
Brody
Brody4mo ago
i changed my mind, we'll try to do this all in a single config file, so lets see if this works.. add a railway.json to your project with this in it:
{
"$schema": "https://schema.up.railway.app/railway.schema.json",
"build": {
"nixpacksPlan": {
"phases": {
"setup": {
"nixPkgs": ["...", "parallel"]
}
}
}
},
"deploy": {
"startCommand": "parallel --ungroup --halt now,fail=1 ::: 'celery -A celery_config worker --loglevel=info --concurrency=1' 'gunicorn main:app'"
}
}
{
"$schema": "https://schema.up.railway.app/railway.schema.json",
"build": {
"nixpacksPlan": {
"phases": {
"setup": {
"nixPkgs": ["...", "parallel"]
}
}
}
},
"deploy": {
"startCommand": "parallel --ungroup --halt now,fail=1 ::: 'celery -A celery_config worker --loglevel=info --concurrency=1' 'gunicorn main:app'"
}
}
CallMeFred
CallMeFred4mo ago
OK, just one thing, I don't know if it has an impact. I have a nickpacks.toml [phases.setup] nixPkgs = ["...", "ffmpeg"] because I'm using ffmepg in the project Do you need to add anything to the JSON for that?
Brody
Brody4mo ago
i think they'd get merged, lets find out
CallMeFred
CallMeFred4mo ago
OK, I will deploy this JSON Building... While we're waiting, you told me that S3 wasn't your preferred option for file storage. Anything special to suggest?
Brody
Brody4mo ago
i only said that with the assumption you would have used an s3 storage provider not hosted on railway, for what you're doing using minio on railway would be the best choice if you needed s3
CallMeFred
CallMeFred4mo ago
I didn't know about minio I'm testing the new setup It worked alleluia
Brody
Brody4mo ago
this isnt my first rodeo
CallMeFred
CallMeFred4mo ago
Thanks so much! I understood from your profile that you're a non-Railway volunteer?
Brody
Brody4mo ago
yeah i dont work for railway, just help out
CallMeFred
CallMeFred4mo ago
From what I can see you help out full time. What a dedication 👏 I hope you have free credits for life 🙂
Brody
Brody4mo ago
haha thanks, i do
CallMeFred
CallMeFred4mo ago
I've sent you a TrainCoffee
Brody
Brody4mo ago
thank you very much!
CallMeFred
CallMeFred4mo ago
Extra gift, created with one my apps on Railway, the AI Jingle Maker.
CallMeFred
CallMeFred4mo ago
The one we've fixed today is the upcoming project, the AI Show Maker.
Brody
Brody4mo ago
very cool!
CallMeFred
CallMeFred4mo ago
#labouroflove