mrok 0.2.1__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mrok/http/master.py CHANGED
@@ -2,6 +2,7 @@ import logging
2
2
  import os
3
3
  import signal
4
4
  import threading
5
+ import time
5
6
  from collections.abc import Callable
6
7
  from pathlib import Path
7
8
 
@@ -11,6 +12,10 @@ from watchfiles.run import CombinedProcess, start_process
11
12
 
12
13
  logger = logging.getLogger("mrok.agent")
13
14
 
15
+ MONITOR_THREAD_JOIN_TIMEOUT = 5
16
+ MONITOR_THREAD_CHECK_DELAY = 1
17
+ MONITOR_THREAD_ERROR_DELAY = 3
18
+
14
19
 
15
20
  def print_path(path):
16
21
  try:
@@ -29,7 +34,7 @@ class Master:
29
34
  self.start_fn = start_fn
30
35
  self.workers = workers
31
36
  self.reload = reload
32
- self.worker_processes: list[CombinedProcess] = []
37
+ self.worker_processes: dict[int, CombinedProcess] = {}
33
38
  self.stop_event = threading.Event()
34
39
  self.watch_filter = PythonFilter(ignore_paths=None)
35
40
  self.watcher = watch(
@@ -39,6 +44,7 @@ class Master:
39
44
  yield_on_timeout=True,
40
45
  )
41
46
  self.setup_signals_handler()
47
+ self.monitor_thread = None
42
48
 
43
49
  def setup_signals_handler(self):
44
50
  for sig in (signal.SIGINT, signal.SIGTERM):
@@ -47,26 +53,50 @@ class Master:
47
53
  def handle_signal(self, *args, **kwargs):
48
54
  self.stop_event.set()
49
55
 
56
+ def start_worker(self, worker_id: int):
57
+ """Start a single worker process"""
58
+ p = start_process(
59
+ self.start_fn,
60
+ "function",
61
+ (),
62
+ None,
63
+ )
64
+ logger.info(f"Worker {worker_id} [{p.pid}] started")
65
+ return p
66
+
50
67
  def start(self):
51
- for _ in range(self.workers):
52
- p = start_process(
53
- self.start_fn,
54
- "function",
55
- (),
56
- None,
57
- )
58
- logger.info(f"Worker [{p.pid}] started")
59
- self.worker_processes.append(p)
68
+ for i in range(self.workers):
69
+ p = self.start_worker(i)
70
+ self.worker_processes[i] = p
60
71
 
61
72
  def stop(self):
62
- for process in self.worker_processes:
73
+ for process in self.worker_processes.values():
63
74
  process.stop(sigint_timeout=5, sigkill_timeout=1)
64
- self.worker_processes = []
75
+ self.worker_processes.clear()
65
76
 
66
77
  def restart(self):
67
78
  self.stop()
68
79
  self.start()
69
80
 
81
+ def monitor_workers(self):
82
+ while not self.stop_event.is_set():
83
+ try:
84
+ for worker_id, process in self.worker_processes.items():
85
+ if not process.is_alive():
86
+ logger.warning(f"Worker {worker_id} [{process.pid}] died unexpectedly")
87
+ process.stop(sigint_timeout=1, sigkill_timeout=1)
88
+ new_process = self.start_worker(worker_id)
89
+ self.worker_processes[worker_id] = new_process
90
+ logger.info(
91
+ f"Restarted worker {worker_id} [{process.pid}] -> [{new_process.pid}]"
92
+ )
93
+
94
+ time.sleep(MONITOR_THREAD_CHECK_DELAY)
95
+
96
+ except Exception as e:
97
+ logger.error(f"Error in worker monitoring: {e}")
98
+ time.sleep(MONITOR_THREAD_ERROR_DELAY)
99
+
70
100
  def __iter__(self):
71
101
  return self
72
102
 
@@ -79,12 +109,24 @@ class Master:
79
109
  def run(self):
80
110
  self.start()
81
111
  logger.info(f"Master process started: {os.getpid()}")
82
- if self.reload:
83
- for files_changed in self:
84
- if files_changed:
85
- logger.warning(
86
- f"{', '.join(map(print_path, files_changed))} changed, reloading...",
87
- )
88
- self.restart()
89
- else:
90
- self.stop_event.wait()
112
+
113
+ # Start worker monitoring thread
114
+ self.monitor_thread = threading.Thread(target=self.monitor_workers, daemon=True)
115
+ self.monitor_thread.start()
116
+ logger.debug("Worker monitoring thread started")
117
+
118
+ try:
119
+ if self.reload:
120
+ for files_changed in self:
121
+ if files_changed:
122
+ logger.warning(
123
+ f"{', '.join(map(print_path, files_changed))} changed, reloading...",
124
+ )
125
+ self.restart()
126
+ else:
127
+ self.stop_event.wait()
128
+ finally:
129
+ if self.monitor_thread and self.monitor_thread.is_alive(): # pragma: no cover
130
+ logger.debug("Wait for monitor worker to exit")
131
+ self.monitor_thread.join(timeout=MONITOR_THREAD_JOIN_TIMEOUT)
132
+ self.stop()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mrok
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: MPT Extensions OpenZiti Orchestrator
5
5
  Author: SoftwareOne AG
6
6
  License: Apache License
@@ -48,7 +48,7 @@ mrok/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
48
  mrok/http/config.py,sha256=k8mjvD3ninJn-v1t-co-GSa3upm4b70bWyk3fwdcOh8,2161
49
49
  mrok/http/forwarder.py,sha256=mo-Z8B8Zg6kdDX-lWEiptRv-9kJU9cEdmg6gt6eF0cc,11374
50
50
  mrok/http/lifespan.py,sha256=9qevhD_5Y0f8fGTh2axdfWx7v1K4vnWtiUNyJLesOHE,262
51
- mrok/http/master.py,sha256=o_0Sxe2XuTgVAwvBbWkYcO3HkCcfvYP4rgxcuIDPwXo,2426
51
+ mrok/http/master.py,sha256=TwU78yE_GQecogBs_PDpl3gY7_jWYNIRNaAXRzi0rvY,4152
52
52
  mrok/http/protocol.py,sha256=ap8jbLUvgbAH81ZJZCBkQiYR7mkV_eL3rpfwEkoE8sU,392
53
53
  mrok/http/server.py,sha256=Mj7C85fc-DXp-WTBWaOd7ag808oliLmFBH5bf-G2FHg,370
54
54
  mrok/ziti/__init__.py,sha256=20OWMiexRhOovZOX19zlX87-V78QyWnEnSZfyAftUdE,263
@@ -59,8 +59,8 @@ mrok/ziti/errors.py,sha256=yYCbVDwktnR0AYduqtynIjo73K3HOhIrwA_vQimvEd4,368
59
59
  mrok/ziti/identities.py,sha256=oE_3j6Y4xCr6uKNdprW55bxGsyKnmJt-MrxrylB2Ey4,5388
60
60
  mrok/ziti/pki.py,sha256=o2tySqHC8-7bvFuI2Tqxg9vX6H6ZSxWxfP_9x29e19M,1954
61
61
  mrok/ziti/services.py,sha256=JnznLTHNZjgbFwnBtv7y2XIp4NiQxLVawwP9EfWdVuM,3208
62
- mrok-0.2.1.dist-info/METADATA,sha256=6pEGhIuJ0KJNyG3QXfLouC71PaFpywYGg2AEWrrQFLg,15546
63
- mrok-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
64
- mrok-0.2.1.dist-info/entry_points.txt,sha256=tloXwvU1uJicBJR2h-8HoVclPgwJWDwuREMHN8Zq-nU,38
65
- mrok-0.2.1.dist-info/licenses/LICENSE.txt,sha256=6PaICaoA3yNsZKLv5G6OKqSfLSoX7MakYqTDgJoTCBs,11346
66
- mrok-0.2.1.dist-info/RECORD,,
62
+ mrok-0.2.2.dist-info/METADATA,sha256=aB7i_YKGCGzw0Eo2yOHE5u7uY4Y4wuDaj24245QH3fE,15546
63
+ mrok-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
64
+ mrok-0.2.2.dist-info/entry_points.txt,sha256=tloXwvU1uJicBJR2h-8HoVclPgwJWDwuREMHN8Zq-nU,38
65
+ mrok-0.2.2.dist-info/licenses/LICENSE.txt,sha256=6PaICaoA3yNsZKLv5G6OKqSfLSoX7MakYqTDgJoTCBs,11346
66
+ mrok-0.2.2.dist-info/RECORD,,
File without changes