|
|
|
@ -0,0 +1,88 @@ |
|
|
|
import redis |
|
|
|
|
|
|
|
import uuid |
|
|
|
|
|
|
|
|
|
|
|
class Client(redis.StrictRedis): |
|
|
|
ALL_MANAGERS = "all:managers" |
|
|
|
ALL_JOBS = "all:jobs" |
|
|
|
MANAGER_WORKERS = "%s:workers" |
|
|
|
MANAGER_JOBS = "%s:jobs" |
|
|
|
WORKER_JOBS = "%s:jobs" |
|
|
|
JOB_KEY = "job:%s" |
|
|
|
|
|
|
|
def __init__(self, host="localhost", port=6379, db=0): |
|
|
|
super(Client, self).__init__(host=host, port=port, db=db) |
|
|
|
|
|
|
|
def register_manager(self, name): |
|
|
|
self.sadd(self.ALL_MANAGERS, name) |
|
|
|
|
|
|
|
def deregister_manager(self, name): |
|
|
|
self.srem(self.ALL_MANAGERS, name) |
|
|
|
|
|
|
|
def register_worker(self, manager, name): |
|
|
|
self.sadd(self.MANAGER_WORKERS % (manager, ), name) |
|
|
|
|
|
|
|
def deregister_worker(self, manager, name): |
|
|
|
self.srem(self.MANAGER_WORKERS % (manager, ), name) |
|
|
|
|
|
|
|
def queue_job(self, job_data, manager=None, worker=None): |
|
|
|
job_id = uuid.uuid4() |
|
|
|
self.hmset(self.JOB_KEY % (job_id, ), job_data) |
|
|
|
if manager is not None: |
|
|
|
self.lpush(self.MANAGER_JOBS % (manager, ), job_id) |
|
|
|
elif worker is not None: |
|
|
|
self.lpush(self.WORKER_JOBS % (worker, ), job_id) |
|
|
|
else: |
|
|
|
self.lpush(self.ALL_JOBS, job_id) |
|
|
|
|
|
|
|
return job_id |
|
|
|
|
|
|
|
def fetch_next_job(self, manager, worker, timeout=10): |
|
|
|
# try to fetch in this order |
|
|
|
# 1) any jobs already assigned but not finished by the worker |
|
|
|
# 2) any jobs assigned specifically to the manager |
|
|
|
# 3) try to grab a job from the pool of all jobs |
|
|
|
job_id = ( |
|
|
|
self.lpop(self.WORKER_JOBS % (worker, )) or |
|
|
|
self.brpoplpush(self.MANAGER_JOBS % (manager, ), self.WORKER_JOBS % (worker, ), timeout=timeout) or |
|
|
|
self.brpoplpush(self.ALL_JOBS, self.WORKER_JOBS % (worker, ), timeout=timeout) |
|
|
|
) |
|
|
|
|
|
|
|
job_data = None |
|
|
|
if job_id is not None: |
|
|
|
job_data = self.hgetall(self.JOB_KEY % (job_id, )) |
|
|
|
|
|
|
|
return (job_id, job_data) |
|
|
|
|
|
|
|
def finish_job(self, job_id, worker_name): |
|
|
|
self.delete(self.JOB_KEY % (job_id, )) |
|
|
|
self.lrem(self.WORKER_JOBS % (worker_name, ), 1, job_id) |
|
|
|
|
|
|
|
def get_all_managers(self): |
|
|
|
return self.lrange(self.ALL_MANAGERS, 0, -1) |
|
|
|
|
|
|
|
def get_manager_workers(self, manager_name): |
|
|
|
return self.lrange(self.MANAGER_WORKERS % (manager_name, ), 0, -1) |
|
|
|
|
|
|
|
def get_worker_pending_jobs(self, worker_name): |
|
|
|
for job_id in self.lrange(self.WORKER_JOBS % (worker_name, ), 0, -1): |
|
|
|
yield job_id |
|
|
|
|
|
|
|
def get_manager_queued_jobs(self, manager_name): |
|
|
|
for job_id in self.lrange(self.MANAGER_JOBS % (manager_name, ), 0, -1): |
|
|
|
yield job_id |
|
|
|
|
|
|
|
def get_all_queued_jobs(self): |
|
|
|
for job_id in self.lrange(self.ALL_JOBS, 0, -1): |
|
|
|
yield (None, job_id) |
|
|
|
|
|
|
|
for manager in self.get_all_managers(): |
|
|
|
for job_id in self.get_manager_queued_jobs(manager): |
|
|
|
yield (manager, job_id) |
|
|
|
|
|
|
|
def get_all_pending_jobs(self): |
|
|
|
for manager in self.get_all_managers(): |
|
|
|
for worker in self.get_manager_workers(manager): |
|
|
|
for job_id in self.get_worker_pending_jobs(worker): |
|
|
|
yield (worker, job_id) |