This repository has been archived by the owner on Apr 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Refactor to use concurrent.futures #12
Merged
Merged
Changes from 25 commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
cd1b76d
Move function fully qualified name saving from the Client's responsib…
aronasorman a9fb3fa
Add SuccessMessage type
aronasorman 4129774
Simplify Worker backend by using concurrent.futures.
aronasorman 72cbc9a
Implement InfiniteLoopThread, a generic thread that runs a function f…
aronasorman d1f9e40
Add timeout parameter to messaging.pop abstractmethod
aronasorman b6c8a62
fix job completion marking
aronasorman 07d5f5c
flesh out worker sending of success message.
aronasorman f332128
Add the queued job state, and give a better string representation of …
aronasorman a16ff34
fix bug InfiniteLoopThread only sleeps when we get an exception.
aronasorman 8c53e89
properly mark jobs as running, then take them off the queue.
aronasorman d3e9a86
catch a possible timeout exception from waiting for a job update.
aronasorman 881679b
Extend get_lambda_to_execute to support passing in a function for pro…
aronasorman 77a2b53
Create a ProgressMessage class for updating our progress.
aronasorman b440efa
Create a new FailureMessage class
aronasorman a0fe416
Allow job progress updates.
aronasorman 7505575
Fetch the job AFTER we get an update about it.
aronasorman e343908
Implement failure notification for jobs.
aronasorman 5b69f13
rename InfiniteLoopThread.cancel to stop
aronasorman 8b06b90
refactor Scheduler to use InfiniteLoopThread instead of defining its …
aronasorman 2fbdaab
add docstrings for the client classes
aronasorman d4bcf4a
remove unused functions and classes
aronasorman 31c5280
add comment documentation for the job's states
aronasorman 920a380
Properly set the initial values of traceback and exception
aronasorman 664bab4
add docstrings for Job.
aronasorman 1875cab
Add initial changelog.
aronasorman 96d7d8e
Merge remote-tracking branch 'origin/develop' into refactor-futures
aronasorman 129c2eb
get tests passing on python27
aronasorman f4b5cdf
Add sqlalchemy and remove enum34
aronasorman 24602c5
truly remove enum dependency
aronasorman a851417
second pass on a SQL-based job storage backend.
aronasorman 2462622
implement barbequeue.exceptions.TimeoutError
aronasorman 8635de4
Fix test that expects MessageType to be an enum
aronasorman ec8f693
use NotImplementedError, not NotImplemented
aronasorman 1b49bb2
make sure read sessions are closed.
aronasorman d02a858
more fixes to get stuff working on sqlalchemy
aronasorman e92205c
add client.all_jobs() and client.clear()
aronasorman 92db474
Properly set orm_job.obj value.
aronasorman 90dca67
allow failed jobs to track the exception and traceback
aronasorman 8f74ba8
Fix clearing of jobs.
aronasorman be50717
Implement SimpleClient to allow users to specify the worker process t…
aronasorman 1554030
remove pdb call from simpletest
aronasorman 391bb9f
Always have concurrent-futures as a dependency.
aronasorman b2687fa
Add wait and wait_for_completion on the client class
aronasorman 92b020b
conditionally import processpool and threadpool.
aronasorman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Change Log | ||
|
||
|
||
All notable changes to this project will be documented in this file. | ||
|
||
The format is based on [Keep a Changelog](http://keepachangelog.com/) | ||
and this project adheres to [Semantic Versioning](http://semver.org/). | ||
|
||
|
||
## [Unreleased] | ||
This comment was marked as spam.
Sorry, something went wrong. |
||
### Added | ||
- the first pass of the small queueing system. | ||
- An InMemClient that spawns thread-based workers, and stores jobs in-memory. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,113 +1,114 @@ | ||
import abc | ||
import enum | ||
import importlib | ||
import logging | ||
import threading | ||
from collections import namedtuple | ||
from functools import partial | ||
|
||
from barbequeue import humanhash | ||
from barbequeue.common.utils import import_stringified_func, stringify_func | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class Job(object): | ||
""" | ||
Job represents a function whose execution has been deferred through the Client's schedule function. | ||
|
||
Jobs are stored on the storage backend for persistence through restarts, and are scheduled for running | ||
to the workers. | ||
""" | ||
class State(enum.Enum): | ||
This comment was marked as spam.
Sorry, something went wrong. |
||
""" | ||
the Job.State object enumerates a Job's possible valid states. | ||
|
||
SCHEDULED means the Job has been accepted by the client, but has not been | ||
sent to the workers for running. | ||
|
||
QUEUED means the Job has been sent to the workers for running, but has not | ||
been run yet (to our knowledge). | ||
|
||
RUNNING means that one of the workers has started running the job, but is not | ||
complete yet. If the job has been set to track progress, then the job's progress | ||
and total_progress fields should be continuously updated. | ||
|
||
FAILED means that the job's function has raised an exception during runtime. | ||
The job's exception and traceback fields should be set. | ||
|
||
CANCELED means that the job has been canceled from running. | ||
|
||
COMPLETED means that the function has run to completion. The job's result field | ||
should be set with the function's return value. | ||
""" | ||
|
||
SCHEDULED = 0 | ||
# STARTED = 1 | ||
QUEUED = 1 | ||
RUNNING = 2 | ||
FAILED = 3 | ||
CANCELED = 4 | ||
COMPLETED = 5 | ||
|
||
def __init__(self, func_string, *args, **kwargs): | ||
def __init__(self, func, *args, **kwargs): | ||
""" | ||
Create a new Job that will run func given the arguments passed to Job(). If the track_progress keyword parameter | ||
is given, the worker will pass an update_progress function to update interested parties about the function's | ||
progress. See Client.__doc__ for update_progress's function parameters. | ||
|
||
:param func: func can be a callable object, in which case it is turned into an importable string, | ||
or it can be an importable string already. | ||
""" | ||
self.job_id = kwargs.pop('job_id', None) | ||
self.state = kwargs.pop('state', self.State.SCHEDULED) | ||
self.func = func_string | ||
self.traceback = kwargs.pop('traceback', '') | ||
self.exception = kwargs.pop('exception', '') | ||
self.traceback = "" | ||
self.exception = None | ||
self.track_progress = kwargs.pop('track_progress', False) | ||
self.progress = 0 | ||
self.total_progress = 0 | ||
self.args = args | ||
self.kwargs = kwargs | ||
|
||
def get_lambda_to_execute(self): | ||
fqn = self.func | ||
modulename, funcname = fqn.rsplit('.', 1) | ||
mod = importlib.import_module(modulename) | ||
assert hasattr( | ||
mod, funcname), \ | ||
"Module {} does not have attribute {}".format( | ||
mod, funcname) | ||
|
||
func = getattr(mod, funcname) | ||
|
||
y = lambda: func(*self.args, **self.kwargs) | ||
return y | ||
if callable(func): | ||
funcstring = stringify_func(func) | ||
elif isinstance(func, str): | ||
funcstring = func | ||
else: | ||
raise Exception( | ||
"Error in creating job. We do not know how to " | ||
"handle a function of type {}".format(type(func))) | ||
|
||
def serialize(self): | ||
pass | ||
|
||
|
||
class ProgressData(namedtuple("_ProgressData", ["id", "order", "data"])): | ||
pass | ||
self.func = funcstring | ||
|
||
def get_lambda_to_execute(self): | ||
""" | ||
return a function that executes the function assigned to this job. | ||
|
||
If job.track_progress is None (the default), the returned function accepts no argument | ||
and simply needs to be called. If job.track_progress is True, an update_progress function | ||
is passed in that can be used by the function to provide feedback progress back to the | ||
job scheduling system. | ||
|
||
:return: a function that executes the original function assigned to this job. | ||
""" | ||
func = import_stringified_func(self.func) | ||
|
||
class Function(namedtuple("_Function", ["module", "funcname"])): | ||
def serialize(self): | ||
# Since this is all in memory, there is no need to serialize anything. | ||
raise NotImplementedError() | ||
if self.track_progress: | ||
y = lambda p: func(update_progress=partial(p, self.job_id), *self.args, **self.kwargs) | ||
else: | ||
y = lambda: func(*self.args, **self.kwargs) | ||
|
||
return y | ||
|
||
class BaseCloseableThread(threading.Thread): | ||
""" | ||
A base class for a thread that monitors an Event as a signal for shutting down, and a main_loop that otherwise loop | ||
until the shutdown event is received. | ||
""" | ||
__metaclass__ = abc.ABCMeta | ||
|
||
DEFAULT_TIMEOUT_SECONDS = 0.2 | ||
|
||
def __init__(self, shutdown_event, thread_name, *args, **kwargs): | ||
assert isinstance(shutdown_event, threading.Event) | ||
|
||
self.shutdown_event = shutdown_event | ||
self.thread_name = thread_name | ||
self.thread_id = self._generate_thread_id() | ||
self.logger = logging.getLogger("{module}.{name}[{id}]".format(module=__name__, | ||
name=self.thread_name, | ||
id=self.thread_id)) | ||
self.full_thread_name = "{thread_name}-{thread_id}".format(thread_name=self.thread_name, | ||
thread_id=self.thread_id) | ||
super(BaseCloseableThread, self).__init__(name=self.full_thread_name, *args, **kwargs) | ||
|
||
def run(self): | ||
self.logger.info("Started new {name} thread ID#{id}".format(name=self.thread_name, | ||
id=self.thread_id)) | ||
|
||
while True: | ||
if self.shutdown_event.wait(self.DEFAULT_TIMEOUT_SECONDS): | ||
self.logger.warning("{name} shut down event received; closing.".format(name=self.thread_name)) | ||
self.shutdown() | ||
break | ||
else: | ||
self.main_loop(timeout=self.DEFAULT_TIMEOUT_SECONDS) | ||
continue | ||
|
||
@abc.abstractmethod | ||
def main_loop(self, timeout): | ||
@property | ||
def percentage_progress(self): | ||
""" | ||
The main loop of a thread. Run this loop if we haven't received any shutdown events in the last | ||
timeout seconds. Normally this is used to read from a queue; you are encouraged to return from | ||
this function if the timeout parameter has elapsed, to allow the thread to continue to check | ||
for the shutdown event. | ||
:param timeout: a parameter determining how long you can wait for a timeout. | ||
:return: None | ||
Returns a float between 0 and 1, representing the current job's progress in its task. | ||
|
||
:return: float corresponding to the total percentage progress of the job. | ||
""" | ||
pass | ||
|
||
@staticmethod | ||
def _generate_thread_id(): | ||
uid, _ = humanhash.uuid() | ||
return uid | ||
|
||
def shutdown(self): | ||
# stub method, override if you need a more complex shut down procedure. | ||
pass | ||
return float(self.progress) / self.total_progress | ||
This comment was marked as spam.
Sorry, something went wrong. |
||
|
||
def __repr__(self): | ||
return "<Job id: {id} state: {state} progress: {p}/{total} func: {func}>".format( | ||
id=self.job_id, | ||
state=self.state.name, | ||
func=self.func, | ||
p=self.progress, | ||
total=self.total_progress | ||
) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This comment was marked as spam.
Sorry, something went wrong.