You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
from .modelsimportJobProcess, JobRequest, JobResult, JobResultStatuses
20
19
from .registryimportjobs_registry
21
20
21
+
ifTYPE_CHECKING:
22
+
from .modelsimportJobResult
23
+
24
+
# Models are NOT imported at the top of this file!
25
+
# See comment on _worker_process_initializer() for explanation.
26
+
22
27
logger=logging.getLogger("plain.jobs")
23
28
24
29
30
+
def_worker_process_initializer() ->None:
31
+
"""Initialize Plain framework in worker process before processing jobs.
32
+
33
+
Why this is needed:
34
+
- We use multiprocessing with 'spawn' context (not 'fork')
35
+
- Spawn creates fresh Python processes, not forked copies
36
+
- When a spawned process starts, it re-imports this module BEFORE the initializer runs
37
+
- If we imported models at the top of this file, model registration would
38
+
happen before plain.runtime.setup(), causing PackageRegistryNotReady errors
39
+
40
+
Solution:
41
+
- This initializer runs plain.runtime.setup() FIRST in each worker process
42
+
- All model imports happen lazily inside functions (after setup completes)
43
+
- This ensures packages registry is ready before any models are accessed
44
+
45
+
Execution order in spawned worker:
46
+
1. Re-import workers.py (but models NOT imported yet - lazy!)
47
+
2. Run this initializer → plain.runtime.setup()
48
+
3. Execute process_job() → NOW it's safe to import models
49
+
"""
50
+
fromplain.runtimeimportsetup
51
+
52
+
# Each spawned worker process needs to set up Plain
53
+
# (spawn context creates fresh processes, not forks)
54
+
setup()
55
+
56
+
25
57
classWorker:
26
58
def__init__(
27
59
self,
@@ -39,6 +71,7 @@ def __init__(
39
71
max_workers=max_processes,
40
72
max_tasks_per_child=max_jobs_per_process,
41
73
mp_context=multiprocessing.get_context("spawn"),
74
+
initializer=_worker_process_initializer,
42
75
)
43
76
44
77
self.queues=queues
@@ -56,6 +89,9 @@ def __init__(
56
89
self._is_shutting_down=False
57
90
58
91
defrun(self) ->None:
92
+
# Lazy import - see _worker_process_initializer() comment for why
93
+
from .modelsimportJobRequest
94
+
59
95
logger.info(
60
96
"⬣ Starting Plain worker\n Registered jobs: %s\n Queues: %s\n Jobs schedule: %s\n Stats every: %s seconds\n Max processes: %s\n Max jobs per process: %s\n Max pending per process: %s\n PID: %s",
0 commit comments