[IMP] base: ir.cron documentation
New API task-4472661
This commit is contained in:
parent
57977d2a10
commit
6a4d0d9d98
@ -658,9 +658,15 @@ Never commit the transaction
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Odoo framework is in charge of providing the transactional context for
|
||||
all RPC calls. The principle is that a new database cursor is opened at the
|
||||
beginning of each RPC call, and committed when the call has returned, just
|
||||
before transmitting the answer to the RPC client, approximately like this:
|
||||
all RPC calls.
|
||||
All ``cr.commit()`` calls outside of the server framework from now on must
|
||||
have an **explicit comment** explaining why they are absolutely necessary, why
|
||||
they are indeed correct, and why they do not break the transactions. Otherwise
|
||||
they can and will be removed!
|
||||
|
||||
The principle is that a new database cursor is opened at the beginning of each
|
||||
RPC call, and committed when the call has returned, just before transmitting the
|
||||
answer to the RPC client, approximately like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -682,8 +688,8 @@ If any error occurs during the execution of the RPC call, the transaction is
|
||||
rolled back atomically, preserving the state of the system.
|
||||
|
||||
Similarly, the system also provides a dedicated transaction during the execution
|
||||
of tests suites, so it can be rolled back or not depending on the server
|
||||
startup options.
|
||||
of tests suites or scheduled actions, so it can be rolled back or not depending
|
||||
on the server startup options.
|
||||
|
||||
The consequence is that if you manually call ``cr.commit()`` anywhere there is
|
||||
a very high chance that you will break the system in various ways, because you
|
||||
@ -697,9 +703,9 @@ among others:
|
||||
during the transaction)
|
||||
|
||||
Here is the very simple rule:
|
||||
You should **NEVER** call ``cr.commit()`` yourself, **UNLESS** you have
|
||||
created your own database cursor explicitly! And the situations where you
|
||||
need to do that are exceptional!
|
||||
You should **NEVER** call ``cr.commit()`` or ``cr.rollback()`` yourself,
|
||||
**UNLESS** you have created your own database cursor explicitly!
|
||||
And the situations where you need to do that are exceptional!
|
||||
|
||||
And by the way if you did create your own cursor, then you need to handle
|
||||
error cases and proper rollback, as well as properly close the cursor when
|
||||
@ -707,20 +713,59 @@ Here is the very simple rule:
|
||||
|
||||
And contrary to popular belief, you do not even need to call ``cr.commit()``
|
||||
in the following situations:
|
||||
|
||||
- in the ``_auto_init()`` method of an *models.Model* object: this is taken
|
||||
care of by the addons initialization method, or by the ORM transaction when
|
||||
creating custom models
|
||||
care of by the addons initialization method, or by the ORM transaction when
|
||||
creating custom models
|
||||
- in reports: the ``commit()`` is handled by the framework too, so you can
|
||||
update the database even from within a report
|
||||
update the database even from within a report
|
||||
- within *models.Transient* methods: these methods are called exactly like
|
||||
regular *models.Model* ones, within a transaction and with the corresponding
|
||||
``cr.commit()/rollback()`` at the end
|
||||
regular *models.Model* ones, within a transaction and with the corresponding
|
||||
``cr.commit()/rollback()`` at the end
|
||||
- etc. (see general rule above if you are in doubt!)
|
||||
|
||||
All ``cr.commit()`` calls outside of the server framework from now on must
|
||||
have an **explicit comment** explaining why they are absolutely necessary, why
|
||||
they are indeed correct, and why they do not break the transactions. Otherwise
|
||||
they can and will be removed !
|
||||
Avoid catching exceptions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As ever, avoid catching too broadly.
|
||||
You may catch too much and prevent error handling from being done properly and
|
||||
uncaught exceptions are already logged or handled by the framework.
|
||||
|
||||
You should be specific of the types you are catching and handle them
|
||||
accordingly and you should limit the scope of your try-catch block as much
|
||||
as possible.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# BAD CODE
|
||||
try:
|
||||
do_something()
|
||||
except Exception as e:
|
||||
# if we caught a ValidationError, we did not rollback and we left the
|
||||
# ORM in an undefined state
|
||||
_logger.warning(e)
|
||||
|
||||
If you are handling framework exceptions, you must at least use **savepoints**
|
||||
to isolate your function as much as possible.
|
||||
It will flush the computations when entering the block and rollback changes
|
||||
properly in case of exceptions.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
with self.env.cr.savepoint():
|
||||
do_stuff()
|
||||
except ...:
|
||||
...
|
||||
|
||||
.. warning::
|
||||
|
||||
After you start more than 64 savepoints during a single transaction,
|
||||
PostgreSQL will slow down.
|
||||
If you process records and savepoint in a loop, for example when processing
|
||||
records one by one for a batch, limit the size of the batch.
|
||||
If you have more records, maybe the function should become a schedule job
|
||||
or you have to accept the performance penalty.
|
||||
|
||||
Use translation method correctly
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -442,34 +442,78 @@ Actions triggered automatically on a predefined frequency.
|
||||
Priority of the action when executing multiple actions at the same time
|
||||
|
||||
|
||||
Advanced use: Batching
|
||||
Writing cron functions
|
||||
----------------------
|
||||
|
||||
When executing a scheduled action, it's recommended to try batching progress in order
|
||||
to avoid hogging a worker for a long period of time and possibly running into timeout exceptions.
|
||||
When executing a scheduled action, it's recommended to try batching progress in
|
||||
order to avoid hogging a worker for a long period of time and possibly running
|
||||
into timeout exceptions. Therefore you should split the processing so that each
|
||||
call will progress in the work to be done.
|
||||
|
||||
Odoo provides a simple API for scheduled action batching;
|
||||
It is the responsiblity of the framework to call the function as many times as
|
||||
needed to process remaining work.
|
||||
When writing a such a function, you should focus on processing a single batch.
|
||||
A batch should process one or many records and should generally take no more
|
||||
than *a few seconds*. Work will be committed by the framework after each batch.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.env['ir.cron']._notify_progress(done=XX:int, remaining=XX:int)
|
||||
@api.cron('xml_id...')
|
||||
def _cron_do_something(self, limit=300): # some default batch limit, allows for tweaking
|
||||
domain = [('state', '=', 'ready')]
|
||||
records = self.search(domain, limit=limit)
|
||||
records.do_something()
|
||||
# notify progression
|
||||
remaining = 0 if len(records) == limit else self.search_count(domain)
|
||||
self.env.cron_progress(done=len(records), remaining=remaining)
|
||||
|
||||
This method allows the scheduler to know if progress was made and whether there is
|
||||
still remaining work that must be done.
|
||||
|
||||
By default, if the API is used, the scheduler tries to process 10 batches in one sitting.
|
||||
If there are still remaining tasks after those 10 batches, a new cron call will be executed as
|
||||
soon as possible.
|
||||
|
||||
Advanced use: Triggers
|
||||
----------------------
|
||||
|
||||
For more complex use cases, Odoo provides a more advanced way to trigger
|
||||
scheduled actions directly from business code.
|
||||
There are some cases, where you may still want to keep resources between cron calls.
|
||||
In such a case, the ``@api.cron`` decorator understands yielding work that should
|
||||
be processed in isolation.
|
||||
The scheduler is still responsible of scheduling the work and stopping when needed.
|
||||
The following shows how to commit after each processed record while keeping an
|
||||
open connection.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
action_record._trigger(at=XX:date)
|
||||
@api.cron('xml_id...')
|
||||
def _cron_do_something(self):
|
||||
# limit is less important as we already give back control to the scheduler
|
||||
domain = [('state', '=', 'ready')]
|
||||
records = self.search(domain)
|
||||
self.env.cron_progress(remaining=len(records))
|
||||
|
||||
with open_some_connection() as conn:
|
||||
def process_record(record):
|
||||
# with_prefetch: keep prefetch to the single processed value
|
||||
# exists: record may disappear while looping and committing
|
||||
# if possible, we could lock the record for update
|
||||
# filtered_domain: record may have changed
|
||||
record = record.with_prefetch().exists().filtered_domain(domain)
|
||||
if record:
|
||||
record.do_something(conn)
|
||||
self.env.cron_progress(1)
|
||||
for record in records:
|
||||
try:
|
||||
yield lambda: process_record(record)
|
||||
except Exception:
|
||||
raise # you may catch it an continue safely
|
||||
|
||||
Running cron functions
|
||||
----------------------
|
||||
|
||||
There are two ways to run functions: immediate (in current thread) or trigger
|
||||
to start at a given time or as soon as possible.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# run now (still in a separate transaction)
|
||||
cron_record.method_direct_trigger()
|
||||
# trigger
|
||||
self._cron_do_something.trigger()
|
||||
|
||||
Security
|
||||
--------
|
||||
|
@ -649,7 +649,7 @@ Method decorators
|
||||
=================
|
||||
|
||||
.. automodule:: odoo.api
|
||||
:members: depends, depends_context, constrains, onchange, autovacuum, model, model_create_multi, private, ondelete
|
||||
:members: depends, depends_context, constrains, onchange, autovacuum, cron, model, model_create_multi, private, ondelete
|
||||
|
||||
.. .. currentmodule:: odoo.api
|
||||
|
||||
@ -659,6 +659,7 @@ Method decorators
|
||||
.. .. autodata:: constrains
|
||||
.. .. autodata:: onchange
|
||||
.. .. autodata:: autovacuum
|
||||
.. .. autodata:: cron
|
||||
|
||||
.. todo:: With sphinx 2.0 : autodecorator
|
||||
|
||||
|
@ -4,6 +4,11 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Odoo Online version 18.3
|
||||
========================
|
||||
|
||||
- New CRON API. TODO
|
||||
|
||||
Odoo Online version 18.2
|
||||
========================
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user