26

I have a Python Flask app on Heroku that serves web pages but also allows certain tasks to be launched which I believe would be best structured as background tasks. As such, I've followed the Heroku rq tutorial to set up background tasks. My Procfile looks like this:

web: python app.py
worker: python worker.py

However, my processes are currently scaled web=1 worker=0. Given that this background process won't be run very often, it doesn't seem sensible to me to provision an entire dyno for it and then pay the $34/month for something that small.

Question:

  • If I leave the worker process declared in my Procfile but keep the scaling at web=1 worker=0, will my queued processes eventually be run on my available web dyno? Or will the queued processes never run?
  • If the queued processes will never run, is there another way to do this short of, for example, using twisted in my web app to run the tasks asynchronously?

Additional Information

worker.py looks like this:

import os
import redis
from rq import Worker, Queue, Connection

listen = ['high', 'default', 'low']

redis_url = os.getenv('REDISTOGO_URL', 'redis://localhost:6379')

conn = redis.from_url(redis_url)

if __name__ == '__main__':
    with Connection(conn):
        worker = Worker(map(Queue, listen))
        worker.work()

The logic in the main app that enqueues a process looks like this:

from rq import Queue
from worker import conn
q = Queue(connection=conn)

q.enqueue(myfunction, myargument)
jdotjdot
  • 16,134
  • 13
  • 66
  • 118
  • I don't know exactly how heroku works, but couldn't you just spawn a worker thread? You could use a Queue to push a job to it... – korylprince Sep 28 '12 at 06:38
  • @korylprince Yes, I could do that--that's what I meant by writing it into the code asynchronously. I'd prefer to do it with the queue if possible, though. – jdotjdot Sep 28 '12 at 07:09

5 Answers5

13

Modify Procfile to look like this:

web: bin/web

Now create the bin directory, and create the file bin/web to look like this:

#!/bin/bash
python app.py &
python worker.py

Make sure you give this file the executable permission:

$ chmod +x bin/web
Flimm
  • 136,138
  • 45
  • 251
  • 267
Kenneth Reitz
  • 8,559
  • 4
  • 31
  • 34
  • 37
    I think I understand what you mean by this, but it would be more helpful both to me and the community if you would explain your answer further. – jdotjdot Sep 28 '12 at 07:11
  • Basically what he's doing is first he run application in the background and then worker in the foreground. – Andrew Feb 02 '13 at 21:45
  • 2
    > "Is this a good practice?" Good practive is to have multiple dynos - that's heroku for. But for one dyno-purposes - why not. – Antigluk Dec 20 '13 at 12:47
  • 1
    Answer is coming from python guy at Heroku. Answer is working fine but more explanation would be good. – iankit Oct 12 '14 at 20:20
  • I have been searching for hours, and I think I understand this answer, but I am not able to find a suitable solution for my needs. How would I achieve having a web site and background process on the same dyno using ruby on rails? – Kevin Horvath Nov 17 '14 at 05:24
  • This is all fine and dandy until heroku shuts your web app down , because no one is acessing it. which ends up stopping your worker – Nuno Furtado Mar 11 '15 at 11:42
  • @NunoFurtado We're already talking about "abusing" the free service a little bit. Note that they don't "shut down" dynos you actually pay for. – Two-Bit Alchemist Sep 03 '15 at 15:30
  • @NunoFurtado you could write a bash script to hit the web app with cURL, at whatever interval you needed, and run it with cron. Or you could just pay the ~$0.0 for a micro instance on AWS – Sam H. Nov 10 '15 at 03:05
  • What happens when the site gets shut down, and the worker stops? Also, I was unable to use this fix because of the file name 'bin/web' -- heroku couldn't find it bash: bin/web: No such file or directory. – Aymon Fournier May 24 '16 at 19:21
3

I am currently running both my web and backend scheduler in Heroku using only 1 dyno.

Idea is to provide one main python script for Heroku to start in 1 dyno. This script is used to start both the web server process(es) and the customer scheduler process(es). You can then define your jobs and add them to the custom scheduler.

APScheduler is used in my case.

This is what I did:

in Procfile:

 web: python run_app.py    #the main startup script

in the run_app.py:

# All the required imports
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
from apscheduler.triggers.cron import CronTrigger
from run_housekeeping import run_housekeeping
from apscheduler.schedulers.background import BackgroundScheduler
import os

def run_web_script():
    # start the gunicorn server with custom configuration
    # You can also using app.run() if you want to use the flask built-in server -- be careful about the port
    os.system('gunicorn -c gunicorn.conf.py web.jobboard:app --debug')  

def start_scheduler():

     # define a background schedule 
     # Attention: you cannot use a blocking scheduler here as that will block the script from proceeding.
     scheduler = BackgroundScheduler()

     # define your job trigger
     hourse_keeping_trigger = CronTrigger(hour='12', minute='30')

     # add your job
     scheduler.add_job(func=run_housekeeping, trigger=hourse_keeping_trigger)

     # start the scheduler
     scheduler.start()


def run():
    start_scheduler()
    run_web_script()

if __name__ == '__main__':
    run()

I am also using 4 Worker processes for serving the web from Gunicorn -- which runs perfectly fine.

In gunicorn.conf.py:

loglevel = 'info'
errorlog = '-'
accesslog = '-'
workers = 4

You may want to checkout this project as an example: Zjobs@Github

Fengzmg
  • 710
  • 8
  • 9
1

You could use a process manager such as god or monit.

With god, you can set up your configuration like so

God.watch do |w|
  w.name = "app"
  w.start = "python app.py"
  w.keepalive
end

God.watch do |w|
  w.name = "worker"
  w.start = "python worker.py"
  w.keepalive
end

Then you put this in your Procfile

god -c path/to/config.god -D

By default, it automatically restarts the process if it crashes, and you can configure it to restart the app if memory usage gets too high. Check out the documentation.

Cameron Martin
  • 5,952
  • 2
  • 40
  • 53
0

To start and run a process in background:

Procfile:

run: python my_app.py

And later, execute:

heroku ps:scale run=1
Alberto
  • 745
  • 1
  • 6
  • 25
-2

You should take a look at Heroku Scheduler it will allow you to run a specific task on a scheduled interval such as every 10 minutes. If you already have your worker setup you could add:

heroku run worker
CraigKerstiens
  • 5,906
  • 1
  • 25
  • 28
  • 1
    I know I could do this, but using Heroku scheduler isn't really the same as having a background queue. That's a cron job, and the 10-minute wait can be far too long for tasks that I just want to push to the background in order not to delay the page loading. – jdotjdot Oct 04 '12 at 08:11
  • 1
    Another option would be to us the heroku API and run a process in a detached mode. By needing an entire queue you'll either need a running process or to have it running in the same Dyno. If you use the heroku API and issue a heroku run command in detached mode it would act almost as if it were a queue. – CraigKerstiens Oct 04 '12 at 16:34
  • Agreed, I was envisioning it running in the same dyno. – jdotjdot Oct 04 '12 at 20:22