vellum-workflow-server 1.8.2__py3-none-any.whl → 1.10.7__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.
workflow_server/start.py CHANGED
@@ -33,6 +33,7 @@ class CustomGunicornLogger(glogging.Logger):
33
33
  logger = logging.getLogger("gunicorn.access")
34
34
  logger.addFilter(HealthCheckFilter())
35
35
  logger.addFilter(SignalFilter())
36
+ logger.addFilter(StatusIsAvailableFilter())
36
37
 
37
38
 
38
39
  class HealthCheckFilter(logging.Filter):
@@ -45,6 +46,11 @@ class SignalFilter(logging.Filter):
45
46
  return "SIGTERM" not in record.getMessage()
46
47
 
47
48
 
49
+ class StatusIsAvailableFilter(logging.Filter):
50
+ def filter(self, record: Any) -> bool:
51
+ return "/status/is_available" not in record.getMessage()
52
+
53
+
48
54
  def start() -> None:
49
55
  if not is_development():
50
56
  start_oom_killer_worker()
@@ -58,8 +64,8 @@ def start() -> None:
58
64
  "workers": int(os.getenv("GUNICORN_WORKERS", 2)),
59
65
  "threads": int(os.getenv("GUNICORN_THREADS", 9 if ENABLE_PROCESS_WRAPPER else 6)),
60
66
  # Aggressively try to avoid memory leaks when using non process mode
61
- "max_requests": 120 if ENABLE_PROCESS_WRAPPER else 20,
62
- "max_requests_jitter": 30 if ENABLE_PROCESS_WRAPPER else 10,
67
+ "max_requests": int(os.getenv("GUNICORN_MAX_REQUESTS", 120 if ENABLE_PROCESS_WRAPPER else 20)),
68
+ "max_requests_jitter": int(os.getenv("GUNICORN_MAX_REQUESTS_JITTER", 30 if ENABLE_PROCESS_WRAPPER else 10)),
63
69
  "worker_class": "gthread",
64
70
  "timeout": max_workflow_runtime_seconds,
65
71
  "logger_class": CustomGunicornLogger,
@@ -1,15 +1,43 @@
1
+ from datetime import datetime
1
2
  import logging
2
3
  import multiprocessing
3
4
  import signal
5
+ from time import sleep
4
6
  from typing import Any
5
7
 
8
+ from workflow_server.config import IS_ASYNC_MODE, is_development
9
+ from workflow_server.utils.system_utils import get_active_process_count
10
+
6
11
  logger = logging.getLogger(__name__)
7
12
  process_killed_switch = multiprocessing.Event()
8
13
 
9
14
 
15
+ def _wait_for_workers() -> None:
16
+ # Would be annoying to have this on for dev since would prevent reload restarts. Also disabling this
17
+ # for non async mode for now since it shouldn't be needed anyway cus we keep the requests open.
18
+ if is_development() and not IS_ASYNC_MODE:
19
+ return
20
+
21
+ start_time = datetime.now()
22
+ loops = 0
23
+
24
+ while get_active_process_count() > 0:
25
+ if loops % 30 == 0:
26
+ logger.info("Waiting for workflow processes to finish...")
27
+
28
+ # TODO needa pass in max workflow time here for VPC
29
+ if (datetime.now() - start_time).total_seconds() > 1800:
30
+ logger.warning("Max elapsed time waiting for workflow processes to complete exceeded, shutting down")
31
+ exit(1)
32
+
33
+ sleep(1)
34
+ loops += 1
35
+
36
+
10
37
  def gunicorn_exit_handler(_worker: Any) -> None:
38
+ logger.info("Received gunicorn kill signal")
11
39
  process_killed_switch.set()
12
- logger.warning("Received gunicorn kill signal")
40
+ _wait_for_workers()
13
41
 
14
42
 
15
43
  def exit_handler(_signal: int, _frame: Any) -> None:
@@ -19,6 +47,7 @@ def exit_handler(_signal: int, _frame: Any) -> None:
19
47
  """
20
48
  process_killed_switch.set()
21
49
  logger.warning("Received kill signal")
50
+ _wait_for_workers()
22
51
  exit(1)
23
52
 
24
53