19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:30:27 +01:00
parent d1963a3c3a
commit 2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions

View file

@ -58,7 +58,7 @@ _REGISTRY_CACHES = {
'routing': 1024, # 2 entries per website
'routing.rewrites': 8192, # url_rewrite entries
'templates.cached_values': 2048, # arbitrary
'groups': 8, # see res.groups
'groups': 64, # see res.groups
}
# cache invalidation dependencies, as follows:
@ -135,6 +135,7 @@ class Registry(Mapping[str, type["BaseModel"]]):
upgrade_modules: Collection[str] = (),
reinit_modules: Collection[str] = (),
new_db_demo: bool | None = None,
models_to_check: set[str] | None = None,
) -> Registry:
"""Create and return a new registry for the given database name.
@ -172,6 +173,19 @@ class Registry(Mapping[str, type["BaseModel"]]):
cls.registries[db_name] = registry # pylint: disable=unsupported-assignment-operation
try:
registry.setup_signaling()
with registry.cursor() as cr:
# This transaction defines a critical section for multi-worker concurrency control.
# When the transaction commits, the first worker proceeds to upgrade modules. Other workers
# encounter a serialization error and retry, finding no upgrade marker in the database.
# This significantly reduces the likelihood of concurrent module upgrades across workers.
# NOTE: This block is intentionally outside the try-except below to prevent workers that fail
# due to serialization errors from calling `reset_modules_state` while the first worker is
# actively upgrading modules.
from odoo.modules import db # noqa: PLC0415
if db.is_initialized(cr):
cr.execute("DELETE FROM ir_config_parameter WHERE key='base.partially_updated_database'")
if cr.rowcount:
update_module = True
# This should be a method on Registry
from odoo.modules.loading import load_modules, reset_modules_state # noqa: PLC0415
exit_stack = ExitStack()
@ -189,6 +203,7 @@ class Registry(Mapping[str, type["BaseModel"]]):
install_modules=install_modules,
reinit_modules=reinit_modules,
new_db_demo=new_db_demo,
models_to_check=models_to_check,
)
except Exception:
reset_modules_state(db_name)
@ -686,6 +701,8 @@ class Registry(Mapping[str, type["BaseModel"]]):
# not already marked as "to be applied".
with cr.savepoint(flush=False):
func(cr)
else:
self._constraint_queue[key] = func
except Exception as e:
if self._is_install:
_schema.error(*e.args)
@ -767,8 +784,7 @@ class Registry(Mapping[str, type["BaseModel"]]):
SELECT c.relname, a.attname
FROM pg_attribute a
JOIN pg_class c ON a.attrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE n.nspname = 'public'
WHERE c.relnamespace = current_schema::regnamespace
AND a.attnotnull = true
AND a.attnum > 0
AND a.attname != 'id';
@ -803,7 +819,8 @@ class Registry(Mapping[str, type["BaseModel"]]):
return
# retrieve existing indexes with their corresponding table
cr.execute("SELECT indexname, tablename FROM pg_indexes WHERE indexname IN %s",
cr.execute("SELECT indexname, tablename FROM pg_indexes WHERE indexname IN %s"
" AND schemaname = current_schema",
[tuple(row[0] for row in expected)])
existing = dict(cr.fetchall())
@ -883,6 +900,7 @@ class Registry(Mapping[str, type["BaseModel"]]):
JOIN pg_attribute AS a1 ON a1.attrelid = c1.oid AND fk.conkey[1] = a1.attnum
JOIN pg_attribute AS a2 ON a2.attrelid = c2.oid AND fk.confkey[1] = a2.attnum
WHERE fk.contype = 'f' AND c1.relname IN %s
AND c1.relnamespace = current_schema::regnamespace
"""
cr.execute(query, [tuple({table for table, column in self._foreign_keys})])
existing = {
@ -969,10 +987,9 @@ class Registry(Mapping[str, type["BaseModel"]]):
query = """
SELECT c.relname
FROM pg_class c
JOIN pg_namespace n ON (n.oid = c.relnamespace)
WHERE c.relname IN %s
AND c.relkind = 'r'
AND n.nspname = 'public'
AND c.relnamespace = current_schema::regnamespace
"""
tables = tuple(m._table for m in self.models.values())
cr.execute(query, [tables])
@ -1006,7 +1023,8 @@ class Registry(Mapping[str, type["BaseModel"]]):
# The `orm_signaling_...` sequences indicates when caches must
# be invalidated (i.e. cleared).
signaling_tables = tuple(f'orm_signaling_{cache_name}' for cache_name in ['registry', *_CACHES_BY_KEY])
cr.execute("SELECT table_name FROM information_schema.tables WHERE table_name IN %s", [signaling_tables])
cr.execute("SELECT table_name FROM information_schema.tables"
" WHERE table_name IN %s AND table_schema = current_schema", [signaling_tables])
existing_sig_tables = tuple(s[0] for s in cr.fetchall()) # could be a set but not efficient with such a little list
# signaling was previously using sequence but this doesn't work with replication