1
1
import threading
2
2
import queue
3
+ import multiprocessing
3
4
from pipeline .file_reading .node_manager import NodeManager
4
5
from pipeline .changes .change_detector import ChangeLevelDetector
5
6
from pipeline .state_builder import StateBuilder
10
11
import sys
11
12
12
13
from common .logger import Logger
13
- logger = Logger (name = __name__ .split ('.' )[- 1 ], log_dir = 'logs' ).get_logger_real ()
14
+ logger = Logger (name = __name__ .split ('.' )[- 1 ], log_dir = 'logs' , rack = sys .argv [1 ]).get_logger_real ()
15
+
16
+ def node_manager_process (buffer_queue , stop_event , limit_nodes , limit_racks , temp_dir , rows_in_mem ):
17
+ """NodeManager process function that can be pickled"""
18
+ node_manager = NodeManager (
19
+ buffer = buffer_queue ,
20
+ limit_nodes = limit_nodes ,
21
+ temp_dir = temp_dir ,
22
+ rows_in_mem = rows_in_mem ,
23
+ limit_racks = limit_racks
24
+ )
25
+ node_manager .iterate_batches (stop_event = stop_event , final_log_frequency = 500 )
26
+
27
+ def change_detector_process (buffer_queue , change_queue , delta , clock ):
28
+ """ChangeDetector process function that can be pickled"""
29
+ change_detector = ChangeLevelDetector (buffer_queue , change_queue , delta = delta , clock = clock )
30
+ change_detector .run ()
31
+
32
+ def state_builder_process (change_queue , state_queue ):
33
+ """StateBuilder process function that can be pickled"""
34
+ state_builder = StateBuilder (change_queue , state_queue )
35
+ state_builder .run ()
36
+
37
+ def state_persister_process (state_queue , output_file ):
38
+ """StatePersister process function that can be pickled"""
39
+ state_persister = StatePersister (state_queue , output_file = output_file )
40
+ state_persister .run ()
14
41
15
42
def run ():
16
43
limit_nodes = None
17
44
limit_racks = int (sys .argv [1 ])
18
45
delta = 0.5
19
46
clock = 3
20
- bq_max_size = 300
21
- rows_in_mem = 300
47
+ rows_in_mem = 1000
48
+ bq_max_size = 2 * rows_in_mem
22
49
temp_dir_loc = "E:/temp_parquet_files"
23
50
24
51
vars_to_log = ['limit_nodes' , 'limit_racks' , 'delta' , 'clock' , 'bq_max_size' , 'rows_in_mem' ]
@@ -30,48 +57,58 @@ def run():
30
57
# Initialize memory monitor
31
58
memory_monitor = MemoryMonitor (log_interval = 50 )
32
59
33
- # Set up queues for each stage with size limits for backpressure
34
- # Create queues with smaller sizes for more aggressive memory management
35
- buffer_queue = queue .Queue (maxsize = bq_max_size ) # NodeManager → ChangeLevelDetector (reduced from 200)
36
- change_queue = queue .Queue (maxsize = 500 ) # ChangeLevelDetector → StateBuilder (reduced from 100)
37
- state_queue = queue .Queue (maxsize = 500 ) # StateBuilder → StatePersister (reduced from 500)
60
+ # Set up queues for each stage with size limits for backpressure
61
+ # Use multiprocessing.Queue for inter-process communication
62
+ buffer_queue = multiprocessing .Queue (maxsize = bq_max_size ) # NodeManager → ChangeLevelDetector
63
+ change_queue = multiprocessing .Queue (maxsize = bq_max_size ) # ChangeLevelDetector → StateBuilder
64
+ state_queue = multiprocessing .Queue (maxsize = bq_max_size ) # StateBuilder → StatePersister
38
65
39
- output_file = f'./outputs/threaded_pipeline_state_{ datetime .datetime .now ().strftime ("%Y-%m-%d_%H-%M-%S" )} .parquet'
66
+ output_file = f'./outputs/threaded_pipeline_state_{ datetime .datetime .now ().strftime ("%Y-%m-%d_%H-%M-%S" )} _rack { limit_racks } .parquet'
40
67
41
68
# Remove output file if it exists
42
69
if os .path .exists (output_file ):
43
70
os .remove (output_file )
44
71
45
- # Create the stop event
46
- stop_event = threading .Event ()
47
-
48
- # Set up pipeline stages
49
- node_manager = NodeManager (buffer = buffer_queue , limit_nodes = limit_nodes , temp_dir = temp_dir_loc , rows_in_mem = rows_in_mem , limit_racks = limit_racks )
50
- change_detector = ChangeLevelDetector (buffer_queue , change_queue , delta = delta , clock = clock )
51
- state_builder = StateBuilder (change_queue , state_queue )
52
- state_persister = StatePersister (state_queue , output_file = output_file )
72
+ # Create the stop event (multiprocessing.Event)
73
+ stop_event = multiprocessing .Event ()
53
74
54
- # Create threads
55
- threads = [
56
- threading .Thread (target = lambda : node_manager .iterate_batches (stop_event = stop_event , final_log_frequency = 5000 ), name = "NodeManagerThread" ),
57
- threading .Thread (target = change_detector .run , name = "ChangeLevelDetectorThread" ),
58
- threading .Thread (target = state_builder .run , name = "StateBuilderThread" ),
59
- threading .Thread (target = state_persister .run , name = "StatePersisterThread" ),
75
+ # Create processes with function-based targets that can be pickled
76
+ processes = [
77
+ multiprocessing .Process (
78
+ target = node_manager_process ,
79
+ args = (buffer_queue , stop_event , limit_nodes , limit_racks , temp_dir_loc , rows_in_mem ),
80
+ name = "NodeManagerProcess"
81
+ ),
82
+ multiprocessing .Process (
83
+ target = change_detector_process ,
84
+ args = (buffer_queue , change_queue , delta , clock ),
85
+ name = "ChangeLevelDetectorProcess"
86
+ ),
87
+ multiprocessing .Process (
88
+ target = state_builder_process ,
89
+ args = (change_queue , state_queue ),
90
+ name = "StateBuilderProcess"
91
+ ),
92
+ multiprocessing .Process (
93
+ target = state_persister_process ,
94
+ args = (state_queue , output_file ),
95
+ name = "StatePersisterProcess"
96
+ ),
60
97
]
61
98
62
- # Start threads
63
- for t in threads :
64
- t .start ()
99
+ # Start processes
100
+ for p in processes :
101
+ p .start ()
65
102
66
- logger .info (f"Started all threads " )
103
+ logger .info (f"Started all processes " )
67
104
68
105
try :
69
- while any (t .is_alive () for t in threads ):
106
+ while any (p .is_alive () for p in processes ):
70
107
# Monitor memory usage
71
108
memory_monitor .check_memory ("Pipeline-Main" )
72
109
73
- for t in threads :
74
- t .join (timeout = 0.5 )
110
+ for p in processes :
111
+ p .join (timeout = 0.5 )
75
112
except KeyboardInterrupt :
76
113
logger .info ("KeyboardInterrupt received! Setting stop event and sending sentinels." )
77
114
stop_event .set ()
@@ -80,8 +117,8 @@ def run():
80
117
change_queue .put (None )
81
118
state_queue .put (None )
82
119
logger .info ("Sentinels sent to all queues." )
83
- for t in threads :
84
- t .join (timeout = 5 )
120
+ for p in processes :
121
+ p .join (timeout = 5 )
85
122
logger .info ("Pipeline killed by user." )
86
123
87
124
if __name__ == "__main__" :
0 commit comments