1+ import multiprocessing
12import queue
23import threading
34import time
2122from memos .mem_scheduler .schemas .general_schemas import (
2223 DEFAULT_ACT_MEM_DUMP_PATH ,
2324 DEFAULT_CONSUME_INTERVAL_SECONDS ,
25+ DEFAULT_STARTUP_MODE ,
2426 DEFAULT_THREAD_POOL_MAX_WORKERS ,
27+ STARTUP_BY_PROCESS ,
2528 MemCubeID ,
2629 TreeTextMemory_SEARCH_METHOD ,
2730 UserID ,
@@ -64,6 +67,11 @@ def __init__(self, config: BaseSchedulerConfig):
6467 "thread_pool_max_workers" , DEFAULT_THREAD_POOL_MAX_WORKERS
6568 )
6669
70+ # startup mode configuration
71+ self .scheduler_startup_mode = self .config .get (
72+ "scheduler_startup_mode" , DEFAULT_STARTUP_MODE
73+ )
74+
6775 self .retriever : SchedulerRetriever | None = None
6876 self .db_engine : Engine | None = None
6977 self .monitor : SchedulerGeneralMonitor | None = None
@@ -88,7 +96,8 @@ def __init__(self, config: BaseSchedulerConfig):
8896 self ._web_log_message_queue : Queue [ScheduleLogForWebItem ] = Queue (
8997 maxsize = self .max_web_log_queue_size
9098 )
91- self ._consumer_thread = None # Reference to our consumer thread
99+ self ._consumer_thread = None # Reference to our consumer thread/process
100+ self ._consumer_process = None # Reference to our consumer process
92101 self ._running = False
93102 self ._consume_interval = self .config .get (
94103 "consume_interval_seconds" , DEFAULT_CONSUME_INTERVAL_SECONDS
@@ -574,10 +583,10 @@ def _message_consumer(self) -> None:
574583
575584 def start (self ) -> None :
576585 """
577- Start the message consumer thread and initialize dispatcher resources.
586+ Start the message consumer thread/process and initialize dispatcher resources.
578587
579588 Initializes and starts:
580- 1. Message consumer thread
589+ 1. Message consumer thread or process (based on startup_mode)
581590 2. Dispatcher thread pool (if parallel dispatch enabled)
582591 """
583592 if self ._running :
@@ -590,32 +599,57 @@ def start(self) -> None:
590599 f"Initializing dispatcher thread pool with { self .thread_pool_max_workers } workers"
591600 )
592601
593- # Start consumer thread
602+ # Start consumer based on startup mode
594603 self ._running = True
595- self ._consumer_thread = threading .Thread (
596- target = self ._message_consumer ,
597- daemon = True ,
598- name = "MessageConsumerThread" ,
599- )
600- self ._consumer_thread .start ()
601- logger .info ("Message consumer thread started" )
604+
605+ if self .scheduler_startup_mode == STARTUP_BY_PROCESS :
606+ # Start consumer process
607+ self ._consumer_process = multiprocessing .Process (
608+ target = self ._message_consumer ,
609+ daemon = True ,
610+ name = "MessageConsumerProcess" ,
611+ )
612+ self ._consumer_process .start ()
613+ logger .info ("Message consumer process started" )
614+ else :
615+ # Default to thread mode
616+ self ._consumer_thread = threading .Thread (
617+ target = self ._message_consumer ,
618+ daemon = True ,
619+ name = "MessageConsumerThread" ,
620+ )
621+ self ._consumer_thread .start ()
622+ logger .info ("Message consumer thread started" )
602623
603624 def stop (self ) -> None :
604625 """Stop all scheduler components gracefully.
605626
606- 1. Stops message consumer thread
627+ 1. Stops message consumer thread/process
607628 2. Shuts down dispatcher thread pool
608629 3. Cleans up resources
609630 """
610631 if not self ._running :
611632 logger .warning ("Memory Scheduler is not running" )
612633 return
613634
614- # Signal consumer thread to stop
635+ # Signal consumer thread/process to stop
615636 self ._running = False
616637
617- # Wait for consumer thread
618- if self ._consumer_thread and self ._consumer_thread .is_alive ():
638+ # Wait for consumer thread or process
639+ if self .scheduler_startup_mode == STARTUP_BY_PROCESS and self ._consumer_process :
640+ if self ._consumer_process .is_alive ():
641+ self ._consumer_process .join (timeout = 5.0 )
642+ if self ._consumer_process .is_alive ():
643+ logger .warning ("Consumer process did not stop gracefully, terminating..." )
644+ self ._consumer_process .terminate ()
645+ self ._consumer_process .join (timeout = 2.0 )
646+ if self ._consumer_process .is_alive ():
647+ logger .error ("Consumer process could not be terminated" )
648+ else :
649+ logger .info ("Consumer process terminated" )
650+ else :
651+ logger .info ("Consumer process stopped" )
652+ elif self ._consumer_thread and self ._consumer_thread .is_alive ():
619653 self ._consumer_thread .join (timeout = 5.0 )
620654 if self ._consumer_thread .is_alive ():
621655 logger .warning ("Consumer thread did not stop gracefully" )
0 commit comments