oca-ocb-core/odoo-bringout-oca-ocb-base/odoo/orm/models_transient.py
Ernad Husremovic 991d2234ca 19.0 vanilla
2025-10-03 18:07:25 +02:00

82 lines
3.6 KiB
Python

import datetime
from odoo.tools import SQL, config, lazy_classproperty
from odoo.tools.constants import GC_UNLINK_LIMIT
from . import decorators as api
from .models import Model
class TransientModel(Model):
""" Model super-class for transient records, meant to be temporarily
persistent, and regularly vacuum-cleaned.
A TransientModel has a simplified access rights management, all users can
create new records, and may only access the records they created. The
superuser has unrestricted access to all TransientModel records.
"""
_auto: bool = True # automatically create database backend
_register: bool = False # not visible in ORM registry, meant to be python-inherited only
_abstract = False # not abstract
_transient = True # transient
# default values for _transient_vacuum()
_transient_max_count = lazy_classproperty(lambda _: int(config.get('osv_memory_count_limit')))
"maximum number of transient records, unlimited if ``0``"
_transient_max_hours = lazy_classproperty(lambda _: float(config.get('transient_age_limit')))
"maximum idle lifetime (in hours), unlimited if ``0``"
@api.autovacuum
def _transient_vacuum(self):
"""Clean the transient records.
This unlinks old records from the transient model tables whenever the
:attr:`_transient_max_count` or :attr:`_transient_max_hours` conditions
(if any) are reached.
Actual cleaning will happen only once every 5 minutes. This means this
method can be called frequently (e.g. whenever a new record is created).
Example with both max_hours and max_count active:
Suppose max_hours = 0.2 (aka 12 minutes), max_count = 20, there are
55 rows in the table, 10 created/changed in the last 5 minutes, an
additional 12 created/changed between 5 and 10 minutes ago, the rest
created/changed more than 12 minutes ago.
- age based vacuum will leave the 22 rows created/changed in the last 12
minutes
- count based vacuum will wipe out another 12 rows. Not just 2,
otherwise each addition would immediately cause the maximum to be
reached again.
- the 10 rows that have been created/changed the last 5 minutes will NOT
be deleted
"""
has_remaining = False
if self._transient_max_hours:
# Age-based expiration
has_remaining |= self._transient_clean_rows_older_than(self._transient_max_hours * 60 * 60)
if self._transient_max_count:
# Count-based expiration
has_remaining |= self._transient_clean_old_rows(self._transient_max_count)
# This method is shared by all transient models therefore,
# return the model name to be logged and if whether there are more rows to process
return self._name, has_remaining
def _transient_clean_old_rows(self, max_count: int) -> bool:
# Check how many rows we have in the table
self._cr.execute(SQL("SELECT count(*) FROM %s", SQL.identifier(self._table)))
[count] = self._cr.fetchone()
if count > max_count:
return self._transient_clean_rows_older_than(300)
return False
def _transient_clean_rows_older_than(self, seconds: int) -> bool:
# Never delete rows used in last 5 minutes
seconds = max(seconds, 300)
now = self.env.cr.now()
domain = [('write_date', '<', now - datetime.timedelta(seconds=seconds))]
records = self.sudo().search(domain, limit=GC_UNLINK_LIMIT)
records.unlink()
return len(records) == GC_UNLINK_LIMIT