Circus provides hooks that can be used to trigger actions upon watcher events. Available hooks are:
A typical use case is to control that all the conditions are met for a process to start. Let’s say you have a watcher that runs Redis and a watcher that runs a Python script that works with Redis. With Circus you can order the startup by using the priority option:
[watcher:queue-worker] cmd = python -u worker.py priority = 2 [watcher:redis] cmd = redis-server priority = 1
With this setup, Circus will start Redis first and then it will start the queue worker. But Circus does not really control that Redis is up and running. It just starts the process it was asked to start. What we miss here is a way to control that Redis is started and fully functional. A function that controls this could be:
import redis import time def check_redis(*args, **kw): time.sleep(.5) # give it a chance to start r = redis.StrictRedis(host='localhost', port=6379, db=0) r.set('foo', 'bar') return r.get('foo') == 'bar'
This function can be plugged into Circus as an before_start hook:
[watcher:queue-worker] cmd = python -u worker.py hooks.before_start = mycoolapp.myplugins.check_redis priority = 2 [watcher:redis] cmd = redis-server priority = 1
Once Circus has started the redis watcher, it will start the queue-worker watcher, since it follows the priority ordering. Just before starting the second watcher, it will run the check_redis function, and in case it returns False will abort the watcher starting process.
A hook must follow this signature:
def hook(watcher, arbiter, hook_name, **kwargs): ... # If you don't return True, the hook can change # the behavior of circus (depending on the hook) return True
Where watcher is the Watcher class instance, arbiter the Arbiter one, hook_name the hook name and kwargs some additional optional parameters (depending on the hook type).
The after_spawn hook adds the pid parameters:
def after_spawn(watcher, arbiter, hook_name, pid, **kwargs): ... # If you don't return True, circus will kill the process return True
Where pid is the PID of the corresponding process.
Likewise, before_signal and after_signal hooks add pid and signum:
def before_signal_hook(watcher, arbiter, hook_name, pid, signum, **kwargs): ... # If you don't return True, circus won't send the signum signal # (SIGKILL is always sent) return True
Where pid is the PID of the corresponding process and signum is the corresponding signal.
You can ignore those but being able to use the watcher and/or arbiter data and methods can be useful in some hooks.
Note that hooks are called with named arguments. So use the hook signature without changing argument names.
The extended_stats hook has its own additional parameters in kwargs:
def extended_stats_hook(watcher, arbiter, hook_name, pid, stats, **kwargs): ...
Where pid is the PID of the corresponding process and stats the regular stats to be returned. Add your own stats into stats. An example is in examples/uwsgi_lossless_reload.py.
As a last example, here is a super hook which can deal with all kind of signals:
def super_hook(watcher, arbiter, hook_name, **kwargs): pid = None signum = None if hook_name in ('before_signal', 'after_signal'): pid = kwargs['pid'] signum = kwargs['signum'] ... return True
Everytime a hook is run, its result is notified as an event in Circus.
There are two events related to hooks: