Coverage for adhoc-cicd-odoo-odoo / odoo / orm / models_transient.py: 44%
35 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 18:15 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 18:15 +0000
1import datetime
3from odoo.tools import SQL, config, lazy_classproperty
4from odoo.tools.constants import GC_UNLINK_LIMIT
6from . import decorators as api
7from .models import Model
10class TransientModel(Model):
11 """ Model super-class for transient records, meant to be temporarily
12 persistent, and regularly vacuum-cleaned.
14 A TransientModel has a simplified access rights management, all users can
15 create new records, and may only access the records they created. The
16 superuser has unrestricted access to all TransientModel records.
17 """
18 _auto: bool = True # automatically create database backend
19 _register: bool = False # not visible in ORM registry, meant to be python-inherited only
20 _abstract = False # not abstract
21 _transient = True # transient
23 # default values for _transient_vacuum()
24 _transient_max_count = lazy_classproperty(lambda _: int(config.get('osv_memory_count_limit')))
25 "maximum number of transient records, unlimited if ``0``"
26 _transient_max_hours = lazy_classproperty(lambda _: float(config.get('transient_age_limit')))
27 "maximum idle lifetime (in hours), unlimited if ``0``"
29 @api.autovacuum
30 def _transient_vacuum(self):
31 """Clean the transient records.
33 This unlinks old records from the transient model tables whenever the
34 :attr:`_transient_max_count` or :attr:`_transient_max_hours` conditions
35 (if any) are reached.
37 Actual cleaning will happen only once every 5 minutes. This means this
38 method can be called frequently (e.g. whenever a new record is created).
40 Example with both max_hours and max_count active:
42 Suppose max_hours = 0.2 (aka 12 minutes), max_count = 20, there are
43 55 rows in the table, 10 created/changed in the last 5 minutes, an
44 additional 12 created/changed between 5 and 10 minutes ago, the rest
45 created/changed more than 12 minutes ago.
47 - age based vacuum will leave the 22 rows created/changed in the last 12
48 minutes
49 - count based vacuum will wipe out another 12 rows. Not just 2,
50 otherwise each addition would immediately cause the maximum to be
51 reached again.
52 - the 10 rows that have been created/changed the last 5 minutes will NOT
53 be deleted
54 """
55 has_remaining = False
56 if self._transient_max_hours:
57 # Age-based expiration
58 has_remaining |= self._transient_clean_rows_older_than(self._transient_max_hours * 60 * 60)
60 if self._transient_max_count:
61 # Count-based expiration
62 has_remaining |= self._transient_clean_old_rows(self._transient_max_count)
63 # This method is shared by all transient models therefore,
64 # return the model name to be logged and if whether there are more rows to process
65 return self._name, has_remaining
67 def _transient_clean_old_rows(self, max_count: int) -> bool:
68 # Check how many rows we have in the table
69 self._cr.execute(SQL("SELECT count(*) FROM %s", SQL.identifier(self._table)))
70 [count] = self._cr.fetchone()
71 if count > max_count:
72 return self._transient_clean_rows_older_than(300)
73 return False
75 def _transient_clean_rows_older_than(self, seconds: int) -> bool:
76 # Never delete rows used in last 5 minutes
77 seconds = max(seconds, 300)
78 now = self.env.cr.now()
79 domain = [('write_date', '<', now - datetime.timedelta(seconds=seconds))]
80 records = self.sudo().search(domain, limit=GC_UNLINK_LIMIT)
81 records.unlink()
82 return len(records) == GC_UNLINK_LIMIT