details: https://code.tryton.org/tryton/commit/480eeda38b22
branch: 8.0
user: Cédric Krier <[email protected]>
date: Tue May 19 12:29:44 2026 +0200
description:
Support pool process with spawn context for cron and worker
When max_tasks_per_child is set, the ProcessPoolExecutor uses a spawn
multiprocessing context which starts a new process from scratch with a
__name__
being __mp_main__ which prevents the configuration to be restored.
So in the ProcessPoolExecutor initializer the configuration is restored
using
the initial options.
Closes #14843
(grafted from cb5c6667ac51818d49ca397347419a40b88768fd)
diffstat:
trytond/bin/trytond-cron | 4 +++-
trytond/bin/trytond-worker | 4 +++-
trytond/trytond/cron.py | 11 ++++++++---
trytond/trytond/worker.py | 15 +++++++++++----
4 files changed, 25 insertions(+), 9 deletions(-)
diffs (125 lines):
diff -r f53b3aed8e65 -r 480eeda38b22 trytond/bin/trytond-cron
--- a/trytond/bin/trytond-cron Thu May 21 10:26:44 2026 +0200
+++ b/trytond/bin/trytond-cron Tue May 19 12:29:44 2026 +0200
@@ -13,4 +13,6 @@
from trytond.cli.cron import main # noqa: E402
-main()
+# Ensure main module can be safely imported by a new interpreter
+if __name__ == '__main__':
+ main()
diff -r f53b3aed8e65 -r 480eeda38b22 trytond/bin/trytond-worker
--- a/trytond/bin/trytond-worker Thu May 21 10:26:44 2026 +0200
+++ b/trytond/bin/trytond-worker Tue May 19 12:29:44 2026 +0200
@@ -12,4 +12,6 @@
from trytond.cli.worker import main # noqa: E402
-main()
+# Ensure main module can be safely imported by a new interpreter
+if __name__ == '__main__':
+ main()
diff -r f53b3aed8e65 -r 480eeda38b22 trytond/trytond/cron.py
--- a/trytond/trytond/cron.py Thu May 21 10:26:44 2026 +0200
+++ b/trytond/trytond/cron.py Tue May 19 12:29:44 2026 +0200
@@ -8,6 +8,8 @@
from concurrent import futures
from multiprocessing import cpu_count
+import trytond.commandline as commandline
+import trytond.config as config
from trytond.pool import Pool
from trytond.transaction import Transaction
@@ -25,7 +27,7 @@
max_workers=processes,
mp_context=None,
initializer=initializer,
- initargs=(options.database_names,),
+ initargs=(options,),
max_tasks_per_child=options.maxtasksperchild or None,
)
if sys.version_info < (3, 11):
@@ -42,12 +44,15 @@
time.sleep(60 - (now.second + now.microsecond / 10**6))
-def initializer(database_names, worker=True):
+def initializer(options, worker=True):
+ # restore configuration when mp context is spawn
+ config.update_etc(options.configfile)
+ commandline.config_log(options)
if worker:
signal.signal(signal.SIGINT, signal.SIG_IGN)
pools = []
database_list = Pool.database_list()
- for database_name in database_names:
+ for database_name in options.database_names:
pool = Pool(database_name)
if database_name not in database_list:
with Transaction().start(database_name, 0, readonly=True):
diff -r f53b3aed8e65 -r 480eeda38b22 trytond/trytond/worker.py
--- a/trytond/trytond/worker.py Thu May 21 10:26:44 2026 +0200
+++ b/trytond/trytond/worker.py Tue May 19 12:29:44 2026 +0200
@@ -12,7 +12,8 @@
from sql import Flavor
-from trytond import backend, config
+import trytond.commandline as commandline
+import trytond.config as config
from trytond.exceptions import UserError, UserWarning
from trytond.pool import Pool
from trytond.status import processing
@@ -24,6 +25,7 @@
class Queue(object):
def __init__(self, database_name, executor):
+ from trytond import backend
self.database = backend.Database(database_name).connect()
self.connection = self.database.get_connection(autocommit=True)
self.executor = executor
@@ -57,6 +59,7 @@
def work(options):
+ from trytond import backend
Flavor.set(backend.Database.flavor)
if not config.getboolean('queue', 'worker', default=False):
return
@@ -69,7 +72,7 @@
max_workers=processes,
mp_context=None,
initializer=initializer,
- initargs=(options.database_names,),
+ initargs=(options,),
max_tasks_per_child=options.maxtasksperchild or None,
)
if sys.version_info < (3, 11):
@@ -112,12 +115,15 @@
queue.get_notifications()
-def initializer(database_names, worker=True):
+def initializer(options, worker=True):
+ # restore configuration when mp context is spawn
+ config.update_etc(options.configfile)
+ commandline.config_log(options)
if worker:
signal.signal(signal.SIGINT, signal.SIG_IGN)
pools = []
database_list = Pool.database_list()
- for database_name in database_names:
+ for database_name in options.database_names:
pool = Pool(database_name)
if database_name not in database_list:
with Transaction().start(database_name, 0, readonly=True):
@@ -127,6 +133,7 @@
def run_task(pool, task_id):
+ from trytond import backend
if not isinstance(pool, Pool):
database_list = Pool.database_list()
pool = Pool(pool)