1##########
2User guide
3##########
4
5
6Installing APScheduler
7----------------------
8
9The preferred installation method is by using `pip <http://pypi.python.org/pypi/pip/>`_::
10
11    $ pip install apscheduler
12
13If you don't have pip installed, you can easily install it by downloading and running
14`get-pip.py <https://bootstrap.pypa.io/get-pip.py>`_.
15
16If, for some reason, pip won't work, you can manually `download the APScheduler distribution
17<https://pypi.python.org/pypi/APScheduler/>`_ from PyPI, extract and then install it::
18
19    $ python setup.py install
20
21
22Code examples
23-------------
24
25The source distribution contains the :file:`examples` directory where you can find many working
26examples for using APScheduler in different ways. The examples can also be
27`browsed online <https://github.com/agronholm/apscheduler/tree/3.x/examples/?at=master>`_.
28
29
30Basic concepts
31--------------
32
33APScheduler has four kinds of components:
34
35* triggers
36* job stores
37* executors
38* schedulers
39
40*Triggers* contain the scheduling logic. Each job has its own trigger which determines when the job
41should be run next. Beyond their initial configuration, triggers are completely stateless.
42
43*Job stores* house the scheduled jobs. The default job store simply keeps the jobs in memory, but
44others store them in various kinds of databases. A job's data is serialized when it is saved to a
45persistent job store, and deserialized when it's loaded back from it. Job stores (other than the
46default one) don't keep the job data in memory, but act as middlemen for saving, loading, updating
47and searching jobs in the backend. Job stores must never be shared between schedulers.
48
49*Executors* are what handle the running of the jobs. They do this typically by submitting the
50designated callable in a job to a thread or process pool. When the job is done, the executor
51notifies the scheduler which then emits an appropriate event.
52
53*Schedulers* are what bind the rest together. You typically have only one scheduler running in your
54application. The application developer doesn't normally deal with the job stores, executors or
55triggers directly. Instead, the scheduler provides the proper interface to handle all those.
56Configuring the job stores and executors is done through the scheduler, as is adding, modifying and
57removing jobs.
58
59
60Choosing the right scheduler, job store(s), executor(s) and trigger(s)
61----------------------------------------------------------------------
62
63Your choice of scheduler depends mostly on your programming environment and what you'll be using
64APScheduler for. Here's a quick guide for choosing a scheduler:
65
66* :class:`~apscheduler.schedulers.blocking.BlockingScheduler`:
67  use when the scheduler is the only thing running in your process
68* :class:`~apscheduler.schedulers.background.BackgroundScheduler`:
69  use when you're not using any of the frameworks below, and want the scheduler to run in the
70  background inside your application
71* :class:`~apscheduler.schedulers.asyncio.AsyncIOScheduler`:
72  use if your application uses the asyncio module
73* :class:`~apscheduler.schedulers.gevent.GeventScheduler`:
74  use if your application uses gevent
75* :class:`~apscheduler.schedulers.tornado.TornadoScheduler`:
76  use if you're building a Tornado application
77* :class:`~apscheduler.schedulers.twisted.TwistedScheduler`:
78  use if you're building a Twisted application
79* :class:`~apscheduler.schedulers.qt.QtScheduler`:
80  use if you're building a Qt application
81
82Simple enough, yes?
83
84To pick the appropriate job store, you need to determine whether you need job persistence or not.
85If you always recreate your jobs at the start of your application, then you can probably go with
86the default (:class:`~apscheduler.jobstores.memory.MemoryJobStore`). But if you need your jobs to
87persist over scheduler restarts or application crashes, then your choice usually boils down to what
88tools are used in your programming environment. If, however, you are in the position to choose
89freely, then :class:`~apscheduler.jobstores.sqlalchemy.SQLAlchemyJobStore` on a
90`PostgreSQL <http://www.postgresql.org/>`_ backend is the recommended choice due to its strong data
91integrity protection.
92
93Likewise, the choice of executors is usually made for you if you use one of the frameworks above.
94Otherwise, the default :class:`~apscheduler.executors.pool.ThreadPoolExecutor` should be good
95enough for most purposes. If your workload involves CPU intensive operations, you should consider
96using :class:`~apscheduler.executors.pool.ProcessPoolExecutor` instead to make use of multiple CPU
97cores. You could even use both at once, adding the process pool executor as a secondary executor.
98
99When you schedule a job, you need to choose a *trigger* for it. The trigger determines the logic by
100which the dates/times are calculated when the job will be run. APScheduler comes with three
101built-in trigger types:
102
103* :mod:`~apscheduler.triggers.date`:
104  use when you want to run the job just once at a certain point of time
105* :mod:`~apscheduler.triggers.interval`:
106  use when you want to run the job at fixed intervals of time
107* :mod:`~apscheduler.triggers.cron`:
108  use when you want to run the job periodically at certain time(s) of day
109
110It is also possible to combine multiple triggers into one which fires either on times agreed on by
111all the participating triggers, or when any of the triggers would fire. For more information, see
112the documentation for :mod:`combining triggers <apscheduler.triggers.combining>`.
113
114You can find the plugin names of each job store, executor and trigger type on their respective API
115documentation pages.
116
117
118.. _scheduler-config:
119
120Configuring the scheduler
121-------------------------
122
123APScheduler provides many different ways to configure the scheduler. You can use a configuration
124dictionary or you can pass in the options as keyword arguments. You can also instantiate the
125scheduler first, add jobs and configure the scheduler afterwards. This way you get maximum
126flexibility for any environment.
127
128The full list of scheduler level configuration options can be found on the API reference of the
129:class:`~apscheduler.schedulers.base.BaseScheduler` class. Scheduler subclasses may also have
130additional options which are documented on their respective API references. Configuration options
131for individual job stores and executors can likewise be found on their API reference pages.
132
133Let's say you want to run BackgroundScheduler in your application with the default job store and
134the default executor::
135
136    from apscheduler.schedulers.background import BackgroundScheduler
137
138
139    scheduler = BackgroundScheduler()
140
141    # Initialize the rest of the application here, or before the scheduler initialization
142
143This will get you a BackgroundScheduler with a MemoryJobStore named "default" and a
144ThreadPoolExecutor named "default" with a default maximum thread count of 10.
145
146Now, suppose you want more. You want to have *two* job stores using *two* executors and you also
147want to tweak the default values for new jobs and set a different timezone.
148The following three examples are completely equivalent, and will get you:
149
150* a MongoDBJobStore named "mongo"
151* an SQLAlchemyJobStore named "default" (using SQLite)
152* a ThreadPoolExecutor named "default", with a worker count of 20
153* a ProcessPoolExecutor named "processpool", with a worker count of 5
154* UTC as the scheduler's timezone
155* coalescing turned off for new jobs by default
156* a default maximum instance limit of 3 for new jobs
157
158Method 1::
159
160    from pytz import utc
161
162    from apscheduler.schedulers.background import BackgroundScheduler
163    from apscheduler.jobstores.mongodb import MongoDBJobStore
164    from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
165    from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
166
167
168    jobstores = {
169        'mongo': MongoDBJobStore(),
170        'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
171    }
172    executors = {
173        'default': ThreadPoolExecutor(20),
174        'processpool': ProcessPoolExecutor(5)
175    }
176    job_defaults = {
177        'coalesce': False,
178        'max_instances': 3
179    }
180    scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
181
182Method 2::
183
184    from apscheduler.schedulers.background import BackgroundScheduler
185
186
187    # The "apscheduler." prefix is hard coded
188    scheduler = BackgroundScheduler({
189        'apscheduler.jobstores.mongo': {
190             'type': 'mongodb'
191        },
192        'apscheduler.jobstores.default': {
193            'type': 'sqlalchemy',
194            'url': 'sqlite:///jobs.sqlite'
195        },
196        'apscheduler.executors.default': {
197            'class': 'apscheduler.executors.pool:ThreadPoolExecutor',
198            'max_workers': '20'
199        },
200        'apscheduler.executors.processpool': {
201            'type': 'processpool',
202            'max_workers': '5'
203        },
204        'apscheduler.job_defaults.coalesce': 'false',
205        'apscheduler.job_defaults.max_instances': '3',
206        'apscheduler.timezone': 'UTC',
207    })
208
209Method 3::
210
211    from pytz import utc
212
213    from apscheduler.schedulers.background import BackgroundScheduler
214    from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
215    from apscheduler.executors.pool import ProcessPoolExecutor
216
217
218    jobstores = {
219        'mongo': {'type': 'mongodb'},
220        'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
221    }
222    executors = {
223        'default': {'type': 'threadpool', 'max_workers': 20},
224        'processpool': ProcessPoolExecutor(max_workers=5)
225    }
226    job_defaults = {
227        'coalesce': False,
228        'max_instances': 3
229    }
230    scheduler = BackgroundScheduler()
231
232    # .. do something else here, maybe add jobs etc.
233
234    scheduler.configure(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
235
236
237Starting the scheduler
238----------------------
239
240Starting the scheduler is done by simply calling
241:meth:`~apscheduler.schedulers.base.BaseScheduler.start` on the scheduler. For schedulers other
242than :class:`~apscheduler.schedulers.blocking.BlockingScheduler`, this call will return immediately and
243you can continue the initialization process of your application, possibly adding jobs to the
244scheduler.
245
246For BlockingScheduler, you will only want to call
247:meth:`~apscheduler.schedulers.base.BaseScheduler.start` after you're done with any initialization
248steps.
249
250.. note:: After the scheduler has been started, you can no longer alter its settings.
251
252
253Adding jobs
254-----------
255
256There are two ways to add jobs to a scheduler:
257
258#. by calling :meth:`~apscheduler.schedulers.base.BaseScheduler.add_job`
259#. by decorating a function with :meth:`~apscheduler.schedulers.base.BaseScheduler.scheduled_job`
260
261The first way is the most common way to do it. The second way is mostly a convenience to declare
262jobs that don't change during the application's run time. The
263:meth:`~apscheduler.schedulers.base.BaseScheduler.add_job` method returns a
264:class:`apscheduler.job.Job` instance that you can use to modify or remove the job later.
265
266You can schedule jobs on the scheduler **at any time**. If the scheduler is not yet running when
267the job is added, the job will be scheduled *tentatively* and its first run time will only be
268computed when the scheduler starts.
269
270It is important to note that if you use an executor or job store that serializes the job, it will
271add a couple requirements on your job:
272
273#. The target callable must be globally accessible
274#. Any arguments to the callable must be serializable
275
276Of the builtin job stores, only MemoryJobStore doesn't serialize jobs.
277Of the builtin executors, only ProcessPoolExecutor will serialize jobs.
278
279.. important:: If you schedule jobs in a persistent job store during your application's
280   initialization, you **MUST** define an explicit ID for the job and use ``replace_existing=True``
281   or you will get a new copy of the job every time your application restarts!
282
283.. tip:: To run a job immediately, omit ``trigger`` argument when adding the job.
284
285
286Removing jobs
287-------------
288
289When you remove a job from the scheduler, it is removed from its associated job store and will not
290be executed anymore. There are two ways to make this happen:
291
292#. by calling :meth:`~apscheduler.schedulers.base.BaseScheduler.remove_job` with the job's ID and
293   job store alias
294#. by calling :meth:`~apscheduler.job.Job.remove` on the Job instance you got from
295   :meth:`~apscheduler.schedulers.base.BaseScheduler.add_job`
296
297The latter method is probably more convenient, but it requires that you store somewhere the
298:class:`~apscheduler.job.Job` instance you received when adding the job. For jobs scheduled via the
299:meth:`~apscheduler.schedulers.base.BaseScheduler.scheduled_job`, the first way is the only way.
300
301If the job's schedule ends (i.e. its trigger doesn't produce any further run times), it is
302automatically removed.
303
304Example::
305
306    job = scheduler.add_job(myfunc, 'interval', minutes=2)
307    job.remove()
308
309Same, using an explicit job ID::
310
311    scheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id')
312    scheduler.remove_job('my_job_id')
313
314
315Pausing and resuming jobs
316-------------------------
317
318You can easily pause and resume jobs through either the :class:`~apscheduler.job.Job` instance or
319the scheduler itself. When a job is paused, its next run time is cleared and no further run times
320will be calculated for it until the job is resumed. To pause a job, use either method:
321
322* :meth:`apscheduler.job.Job.pause`
323* :meth:`apscheduler.schedulers.base.BaseScheduler.pause_job`
324
325To resume:
326
327* :meth:`apscheduler.job.Job.resume`
328* :meth:`apscheduler.schedulers.base.BaseScheduler.resume_job`
329
330
331Getting a list of scheduled jobs
332--------------------------------
333
334To get a machine processable list of the scheduled jobs, you can use the
335:meth:`~apscheduler.schedulers.base.BaseScheduler.get_jobs` method. It will return a list of
336:class:`~apscheduler.job.Job` instances. If you're only interested in the jobs contained in a
337particular job store, then give a job store alias as the second argument.
338
339As a convenience, you can use the :meth:`~apscheduler.schedulers.base.BaseScheduler.print_jobs`
340method which will print out a formatted list of jobs, their triggers and next run times.
341
342
343Modifying jobs
344--------------
345
346You can modify any job attributes by calling either :meth:`apscheduler.job.Job.modify` or
347:meth:`~apscheduler.schedulers.base.BaseScheduler.modify_job`. You can modify any Job attributes
348except for ``id``.
349
350Example::
351
352    job.modify(max_instances=6, name='Alternate name')
353
354If you want to reschedule the job -- that is, change its trigger, you can use either
355:meth:`apscheduler.job.Job.reschedule` or
356:meth:`~apscheduler.schedulers.base.BaseScheduler.reschedule_job`.
357These methods construct a new trigger for the job and recalculate its next run time based on the
358new trigger.
359
360Example::
361
362    scheduler.reschedule_job('my_job_id', trigger='cron', minute='*/5')
363
364
365Shutting down the scheduler
366---------------------------
367
368To shut down the scheduler::
369
370    scheduler.shutdown()
371
372By default, the scheduler shuts down its job stores and executors and waits until all currently
373executing jobs are finished. If you don't want to wait, you can do::
374
375    scheduler.shutdown(wait=False)
376
377This will still shut down the job stores and executors but does not wait for any running
378tasks to complete.
379
380
381Pausing/resuming job processing
382-------------------------------
383
384It is possible to pause the processing of scheduled jobs::
385
386    scheduler.pause()
387
388This will cause the scheduler to not wake up until processing is resumed::
389
390    scheduler.resume()
391
392It is also possible to start the scheduler in paused state, that is, without the first wakeup
393call::
394
395    scheduler.start(paused=True)
396
397This is useful when you need to prune unwanted jobs before they have a chance to run.
398
399
400Limiting the number of concurrently executing instances of a job
401----------------------------------------------------------------
402
403By default, only one instance of each job is allowed to be run at the same time.
404This means that if the job is about to be run but the previous run hasn't finished yet, then the
405latest run is considered a misfire. It is possible to set the maximum number of instances for a
406particular job that the scheduler will let run concurrently, by using the ``max_instances`` keyword
407argument when adding the job.
408
409
410.. _missed-job-executions:
411
412Missed job executions and coalescing
413------------------------------------
414
415Sometimes the scheduler may be unable to execute a scheduled job at the time it was scheduled to
416run. The most common case is when a job is scheduled in a persistent job store and the scheduler
417is shut down and restarted after the job was supposed to execute. When this happens, the job is
418considered to have "misfired". The scheduler will then check each missed execution time against the
419job's ``misfire_grace_time`` option (which can be set on per-job basis or globally in the
420scheduler) to see if the execution should still be triggered. This can lead into the job being
421executed several times in succession.
422
423If this behavior is undesirable for your particular use case, it is possible to use `coalescing` to
424roll all these missed executions into one. In other words, if coalescing is enabled for the job and
425the scheduler sees one or more queued executions for the job, it will only trigger it once. No
426misfire events will be sent for the "bypassed" runs.
427
428.. note::
429    If the execution of a job is delayed due to no threads or processes being available in the
430    pool, the executor may skip it due to it being run too late (compared to its originally
431    designated run time). If this is likely to happen in your application, you may want to either
432    increase the number of threads/processes in the executor, or adjust the ``misfire_grace_time``
433    setting to a higher value.
434
435
436.. _scheduler-events:
437
438Scheduler events
439----------------
440
441It is possible to attach event listeners to the scheduler. Scheduler events are fired on certain
442occasions, and may carry additional information in them concerning the details of that particular
443event. It is possible to listen to only particular types of events by giving the appropriate
444``mask`` argument to :meth:`~apscheduler.schedulers.base.BaseScheduler.add_listener`, OR'ing the
445different constants together. The listener callable is called with one argument, the event object.
446
447See the documentation for the :mod:`~apscheduler.events` module for specifics on the available
448events and their attributes.
449
450Example::
451
452    def my_listener(event):
453        if event.exception:
454            print('The job crashed :(')
455        else:
456            print('The job worked :)')
457
458    scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
459
460
461.. _troubleshooting:
462
463Troubleshooting
464---------------
465
466If the scheduler isn't working as expected, it will be helpful to increase the logging level of the
467``apscheduler`` logger to the ``DEBUG`` level.
468
469If you do not yet have logging enabled in the first place, you can do this::
470
471    import logging
472
473    logging.basicConfig()
474    logging.getLogger('apscheduler').setLevel(logging.DEBUG)
475
476This should provide lots of useful information about what's going on inside the scheduler.
477
478Also make sure that you check the :doc:`faq` section to see if your problem already has a solution.
479
480Reporting bugs
481--------------
482
483.. include:: ../README.rst
484   :start-after: Reporting bugs
485                 --------------
486