Ask HN: How would you build a website that runs long background jobs?
I am building a simple website whereby the user uploads data and the backend kicks off a long process (data computation and analytics)? My preliminary approach is to use Python Flask with Celery and RabbitMQ for job execution. Is this a decent approach? Can anyone else recommend alternative/better approaches?
17 comments
[ 3.0 ms ] story [ 61.5 ms ] threadhttps://dockyard.com/blog/2016/05/02/phoenix-tips-and-tricks
The downside being you have to spend time to really learn the actor concurrency model and OTP concepts (things like GenServer and supervision trees) to really harness this power. While not impossible to learn, it sounds like OP is coming from Python, so while Elixir’s syntax is closer to Python (or higher level scripting languages), working with it is conceptually completely different than working in Python.
A generalized solution to this is you want to break down a task (one big long running job) into a bunch of super small jobs (or processes), that are kicked off by a main job. This is so they can execute quickly, and more importantly keeping them idempotent (easier to restart and debug small jobs with little to no state than big jobs with lots of state). Before kicking off the main job, you’ll want to record it starting somewhere (database, redis, genserver, etc.). Then kickoff the main job, if you need to report progress you can have the child jobs update the progress wherever you stored the start info. Then this can be retrieved either by polling some endpoint, or with pubsub and websockets. Then when the final job ends, update the information to mark that it ended and store the results/reference to the results. Then as above this can be retrieved through that endpoint so clients can be notified when their job finished. If you need to keep state across the jobs, you can use a database or something like redis as the central store for the global job data (which you should choose is dependent on whether or not you need the speed of a k-v store or the transactions/locking of a database) -- if you go the Elixir route this would be done with a GenServer instead.
I also couldn’t recommend redis enough, you can use it as a queue as well as a pub sub, eliminating the number of dependencies if you go the Python/Ruby route.
It probably depends on what your exact requirements are, but celery is likely fine. My last project had celery doing batch job processing for a line of business enterprise web app. It was fine and flexible enough to do what we needed (thousands of jobs a week, job scheduling, rabbitmq broker & postgres result store, in use for years).
One thing to be aware of: if you're not running on windows, celery worker processes are forked, and by default python's process abstractions (subprocess etc) will fight against you to prevent you launching processes from a celery worker. This can be worked around but is a bit irritating.
This is probably obvious but you want to ensure your celery worker processes are properly run as e.g. services with good monitoring, and configured to automatically restart if they crash (due to defects in application specific task logic, say).
Some tips / past discussions:
https://khashtamov.com/en/celery-best-practices-practical-ap...
https://denibertovic.com/posts/celery-best-practices/
https://news.ycombinator.com/item?id=7909201
Sadly, the main issue I see with celery in the past is that it is a popular open source project with no income stream to fund development, so at times swathes of reported bugs have been closed with "won't fix ; we don't have the resources".
https://zapier.com/engineering/automating-billions-of-tasks/
So far I’m pretty happy with it. I minimize the need for maintaining a redis + worker etc
I'm using Lambda to kick off an EC2 worker running a Docker image with AWS Fargate. The EC2 instance only runs for the duration of the job (which lasts from 5 to 20 minutes)
My advice to you is to master these tools instead of learning a new language/stack/tool.
I see ppl talking about Elixir, Erlang, Phoenix, etc. It doesn't matter, what matters if for you to deliver. Python is an excellent choice, the tools, you're talking about are solid... the only thing that matters now is for you to deliver.
Essentially it is just a small server that listens on a nanomsg queue for new tasks. Workers can then be created that periodically ping the server to grab a task off the queue. Optimally a nanomsg queue would also be used to queue the tasks out to the workers; I just build it this way since I could implement the entire thing within hours and continue on with my hack week project.
The benefit of using nanomsg over several of the other message queues suggested here is that nanomsg is a brokerless message queue, meaning that there need not be any central queue tracking everything.
It is somewhat ironic in that sense that I essentially built a message broker using it, but it demonstrates the simplicity of using nanomsg.
While the code there is written in Perl, it could easily be ported to any of the other many languages nanomsg has libraries for.
In summary, to handle long running background jobs: 1. Create as many "worker" processes as you need to be able to simultaneously process multiple long running background jobs. 2. Have a way to feed tasks into those workers ( in my case a central task tracker ) 3. Have a way to feed back status of the workers to something central so you can tell what is going on