Package: psycopg2-mq
psycopg2_mqis a message queue implemented on top ofPostgreSQL,SQLAlchemy, andpsycopg2.Currently the library provides only the low-level constructs that can be used
to build a multithreaded worker system. It is broken into two components:psycopg2_mq.MQWorker- a reusable worker object that manages a
single-threaded worker that can accept jobs and execute them. An application
should create worker per thread. It supports an API for thread-safe graceful
shutdown.psycopg2_mq.MQSource- a source object providing a client-side API for
invoking and querying job states.It is expected that these core components are then wrapped into your own
application in any way that you see fit, without dictating a specific CLI
or framework.Data ModelQueuesWorkers run jobs defined in queues. Currently each queue will run jobs
concurrently, while a future version may support serial execution on a
per-queue basis. Each registered queue should contain anexecute_job(job)method.JobsTheexecute_jobmethod of a queue is passed aJobobject containing
the following attributes:idqueuemethodargscursorAs a convenience, there is anextend(**kw)method which can be used to
add extra attributes to the object. This is useful in individual queues to
define a contract between a queue and its methods.CursorsAJobcan be scheduled with acursor_key. There can only be one
pending job and one running job for any cursor. New jobs scheduled while
another one is pending will be ignored and the pending job is returned.Ajob.cursordict is provided to the workers containing the cursor data,
and is saved back to the database when the job is completed. This effectively
gives jobs some persistent, shared state, and serializes all jobs over a given
cursor.Delayed JobsAJobcan be delayed to run in the future by providing adatetimeobject to thewhenargument. This, along with a cursor key, can provide a
nice throttle on how frequently a job runs. For example, delay jobs to run
in 30 seconds with acursor_keyand any jobs that are scheduled in the
meantime will be dropped. The assumption here is that the arguments are
constant and data for execution is in the cursor or another table. As a last
resort, aconflict_resolvercallback can be used to modify properties of
the job when arguments cannot be constant.SchedulesAJobSchedulecan be defined which supportsRFC 5545RRULEschedules. These
are powerful and can support timezones, frequencies based on calendars as well
as simple recurring rules from an anchor time usingDTSTART. Cron jobs
can be converted to this syntax for simpler scenarios.psycopg2-mqworkers will automatically negotiate which worker is responsible
for managing schedules so clustered workers should operate as expected.Example Workerfrompsycopg2_mqimport(MQWorker,make_default_model,)fromsqlalchemyimport(MetaData,create_engine,)importsysclassEchoQueue:defexecute_job(self,job):returnf'hello,{job.args["name"]}from method="{job.method}"'if__name__=='__main__':engine=create_engine(sys.argv[1])metadata=MetaData()model=make_default_model(metadata)worker=MQWorker(engine=engine,queues={'echo':EchoQueue(),},model=model,)worker.run()Example Sourceengine=create_engine()metadata=MetaData()model=make_default_model(metadata)session_factory=sessionmaker()session_factory.configure(bind=engine)dbsession=session_factory()withdbsession.begin():mq=MQSource(dbsession=dbsession,model=model,)job=mq.call('echo','hello',{'name':'Andy'})print(f'queued job={job.id}')Changes0.9 (2023-04-21)Add support for Python 3.10, and 3.11.[breaking] Prevent retrying of collapsible jobs. Require them to be invoked
usingcallinstead for an opportunity to specify aconflict_resolver.Fix a bug in the default model schema in which the collapsible database
index was not marked unique.Copy trace info when retrying a job.Capture the stringified exception to the job result in themessagekey,
alongside the existingtb,exc, andargskeys.The worker was not recognizingcapture_signals=False, causing problems
when running the event loop in other threads.Blackify the codebase and add some real tests. Yay!0.8.3 (2022-04-15)[breaking] RemoveMQWorker.make_job_context.0.8.2 (2022-04-15)Drop Python 3.6 support.[breaking] Require SQLAlchemy 1.4+ and resolve deprecation warnings related to
SQLAlchemy 2.0.[breaking] Renameupdate_job_idtoupdated_job_idin theJobCursormodel.0.8.1 (2022-04-15)Ensure thetraceattribute is populated on theJobContext.AddMQWorker.make_job_contextwhich can be defined to completely override
theJobContextfactory using theJobobject and open database session.0.8.0 (2022-04-15)[breaking] Addupdate_job_idforeign key to theJobCursormodel to
make it possible to know which job last updated the value in the cursor.[breaking] Addtracejson blob to theJobmodel.Support atracejson blob when creating new jobs. This value is available
on the running job context and can be used when creating sub-jobs or when
making requests to external systems to pass through tracing metadata.SeeMQSource.call’s newtraceparameter when creating jobs.
SeeJobContext.traceattribute when handling jobs.Add a standardFailedJobErrorexception which can be raised by jobs to
mark a failure with a custom result object. This is different from unhandled
exceptions that cause theMQWorker.result_from_errormethod to be invoked.0.7.0 (2022-03-03)Fix a corner case with lost jobs attached to cursors. In scenarios where
multiple workers are running, if one loses a database connection then the
other is designed to notice and mark jobs lost. However, it’s possible the
job is not actually lost and the worker can then recover after resuming
its connection, and marking the job running again. In this situation, we
do not want another job to begin on the same cursor. To fix this issue,
new jobs will not be run if another job is marked lost on the same cursor.
You will be required to recover the job by marking it as not lost (probably
failed) first to unblock the rest of the jobs on the cursor.0.6.2 (2022-03-01)Prioritize maintenance work higher than running new jobs.
There was a chicken-and-egg issue where a job would be marked running
but needs to be marked lost. However marking it lost is lower priority than
trying to start new jobs. In the case where a lot of jobs were scheduled
at the same time, the worker always tried to start new jobs and didn’t
run the maintenance so the job never got marked lost, effectively blocking
the queue.0.6.1 (2022-01-15)Fix a bug introduced in the 0.6.0 release when scheduling new jobs.0.6.0 (2022-01-14)[breaking] Add model changes to mark jobs as collapsible.[breaking] Add model changes to the cursor index.Allow multiple pending jobs to be scheduled on the same cursor if either:The queue or method are different from existing pending jobs on the cursor.collapse_on_cursoris set toFalsewhen scheduling the job.0.5.7 (2021-03-07)Add aschedule_idattribute to the job context for use in jobs that want
to know whether they were executed from a schedule or not.0.5.6 (2021-02-28)Some UnicodeDecodeError exceptions raised from jobs could trigger a
serialization failure (UntranslatableCharacter) because it would contain
the sequence\u0000`which, while valid in Python, is not allowed
in postgres. So when dealing with the raw bytes, we’ll decode it with
the replacement character that can be properly stored. Not ideal, but
better than failing to store the error at all.0.5.5 (2021-01-22)Fixed some old code causing the worker lock to release after a job
completed.0.5.4 (2021-01-20)Log at the error level when marking a job as lost.0.5.3 (2021-01-11)Copy theschedule_idinformation to retried jobs.0.5.2 (2021-01-11)[breaking] Requirecall_scheduleto accept an id instead of an object.0.5.1 (2021-01-09)Drop theUNIQUEconstraint on the background joblock_idcolumn.0.5 (2021-01-09)Add a scheduler model with support for emitting periodic jobs based on
RRULE syntax.
Seehttps://github.com/mmerickel/psycopg2_mq/pull/11Enable the workers to coordinate on a per-queue basis who is in control
of scheduling jobs.
Seehttps://github.com/mmerickel/psycopg2_mq/pull/12Reduce the number of advisory locks held from one per job to one per worker.
Seehttps://github.com/mmerickel/psycopg2_mq/pull/120.4.5 (2020-12-22)Use column objects in the insert statement to support ORM-level synonyms,
enabling the schema to have columns with different names.0.4.4 (2019-11-07)Ensure the advisory locks are released when a job completes.0.4.3 (2019-10-31)Ensure maintenance (finding lost jobs) always runs at set intervals defined
by thetimeoutparameter.0.4.2 (2019-10-30)Recover active jobs when the connection is lost by re-locking them
and ensuring they are marked running.0.4.1 (2019-10-30)Attempt to reconnect to the database after losing the connection.
If the reconnect attempt fails then crash.0.4 (2019-10-28)Add aworkercolumn to theJobmodel to track what worker
is handling a job.Add an optionalnameargument toMQWorkerto name the worker -
the value will be recorded in each job.Add athreadsargument (default=``1``) toMQWorkerto support
handling multiple jobs from the same worker instance instead of making a
worker per thread.Addcapture_signalsargument (default=``True``) toMQWorkerwhich
will captureSIGTERM,SIGINTandSIGUSR1. The first two will
trigger graceful shutdown - they will make the process stop handling new
jobs while finishing active jobs. The latter will dump tostderra
JSON dump of the current status of the worker.0.3.3 (2019-10-23)Only save a cursor update if the job is completed successfully.0.3.2 (2019-10-22)Mark lost jobs during timeouts instead of just when a worker starts in order
to catch them earlier.0.3.1 (2019-10-17)When attempting to schedule a job with a cursor and ascheduled_timeearlier than a pending job on the same cursor, the job will be updated to
run at the earlier time.When attempting to schedule a job with a cursor and a pending job already
exists on the same cursor, aconflict_resolverfunction may be
supplied toMQSource.callto update the job properties, merging the
arguments however the user wishes.0.3 (2019-10-15)Add a new columncursor_snapshotto theJobmodel which will
contain the value of the cursor when the job begins.0.2 (2019-10-09)Add cursor support for jobs. This requires a schema migration to add
acursor_keycolumn, a newJobCursormodel, and some new indices.0.1.6 (2019-10-07)Support passing custom kwargs to the job inpsycopg2_mq.MQSource.callto allow custom columns on the job table.0.1.5 (2019-05-17)Fix a regression when serializing errors with strings or cycles.0.1.4 (2019-05-09)More safely serialize exception objects when jobs fail.0.1.3 (2018-09-04)Rename the thread to contain the job id while it’s handling a job.0.1.2 (2018-09-04)RenameJob.paramstoJob.args.0.1.1 (2018-09-04)Makepsycopg2an optional dependency in order to allow apps to depend
onpsycopg2-binaryif they wish.0.1 (2018-09-04)Initial release.