2020from dataclasses import dataclass , field
2121from itertools import groupby
2222from operator import itemgetter
23- from threading import Lock , Thread
23+ from threading import Thread
2424from typing import TYPE_CHECKING , Any
2525from uuid import uuid4
2626
@@ -361,10 +361,6 @@ class DataPartitionStatus:
361361 keys_mapping : dict [str , int ] = field (default_factory = dict ) # key -> global_idx
362362 revert_keys_mapping : dict [int , str ] = field (default_factory = dict ) # global_idx -> key
363363
364- # Threading lock for concurrency control; only for preventing mask operation error when expanding production_status.
365- # No need to strictly lock for every read/write operation since freshness is not critical.
366- data_status_lock : Lock = field (default_factory = Lock )
367-
368364 # Dynamic configuration - these are computed from the current state
369365 @property
370366 def total_samples_num (self ) -> int :
@@ -409,8 +405,7 @@ def register_pre_allocated_indexes(self, allocated_indexes: list[int]):
409405 max_sample_idx = max (allocated_indexes )
410406 required_samples = max_sample_idx + 1
411407
412- with self .data_status_lock :
413- self .ensure_samples_capacity (required_samples )
408+ self .ensure_samples_capacity (required_samples )
414409
415410 logger .debug (f"Pre-allocated indexes in { self .partition_id } : { allocated_indexes } " )
416411
@@ -526,9 +521,8 @@ def update_production_status(
526521 max_sample_idx = max (global_indices ) if global_indices else - 1
527522 required_samples = max_sample_idx + 1
528523
529- with self .data_status_lock :
530- # Ensure we have enough rows
531- self .ensure_samples_capacity (required_samples )
524+ # Ensure we have enough rows
525+ self .ensure_samples_capacity (required_samples )
532526
533527 # Register new fields if needed
534528 new_fields = [f for f in field_names if f not in self .field_name_mapping ]
@@ -538,14 +532,12 @@ def update_production_status(
538532 self .field_name_mapping [f ] = len (self .field_name_mapping )
539533
540534 required_fields = len (self .field_name_mapping )
541- with self .data_status_lock :
542- self .ensure_fields_capacity (required_fields )
535+ self .ensure_fields_capacity (required_fields )
543536
544- with self .data_status_lock :
545- # Update production status
546- if self .production_status is not None and global_indices and field_names :
547- field_indices = [self .field_name_mapping .get (f ) for f in field_names ]
548- self .production_status [torch .tensor (global_indices )[:, None ], torch .tensor (field_indices )] = 1
537+ # Update production status
538+ if self .production_status is not None and global_indices and field_names :
539+ field_indices = [self .field_name_mapping .get (f ) for f in field_names ]
540+ self .production_status [torch .tensor (global_indices )[:, None ], torch .tensor (field_indices )] = 1
549541
550542 # Update field metadata
551543 self ._update_field_metadata (global_indices , field_schema , custom_backend_meta )
@@ -641,8 +633,7 @@ def get_consumption_status(self, task_name: str, mask: bool = False) -> tuple[Te
641633 if partition_global_index .numel () == 0 :
642634 empty_status = self .consumption_status [task_name ].new_zeros (0 )
643635 return partition_global_index , empty_status
644- with self .data_status_lock :
645- self .ensure_samples_capacity (max (partition_global_index ) + 1 )
636+ self .ensure_samples_capacity (max (partition_global_index ) + 1 )
646637 consumption_status = self .consumption_status [task_name ][partition_global_index ]
647638 else :
648639 consumption_status = self .consumption_status [task_name ]
@@ -730,23 +721,22 @@ def scan_data_status(self, field_names: list[str], task_name: str) -> list[int]:
730721 if field_name not in self .field_name_mapping :
731722 return []
732723
733- with self .data_status_lock :
734- row_mask = torch .ones (self .allocated_samples_num , dtype = torch .bool )
724+ row_mask = torch .ones (self .allocated_samples_num , dtype = torch .bool )
735725
736- # Apply consumption filter (exclude already consumed samples)
737- _ , consumption_status = self .get_consumption_status (task_name , mask = False )
738- if consumption_status is not None :
739- unconsumed_mask = consumption_status == 0
740- row_mask &= unconsumed_mask
726+ # Apply consumption filter (exclude already consumed samples)
727+ _ , consumption_status = self .get_consumption_status (task_name , mask = False )
728+ if consumption_status is not None :
729+ unconsumed_mask = consumption_status == 0
730+ row_mask &= unconsumed_mask
741731
742- # Create column mask for requested fields
743- col_mask = torch .zeros (self .allocated_fields_num , dtype = torch .bool )
744- field_indices = [self .field_name_mapping [field ] for field in field_names ]
745- if field_indices :
746- col_mask [field_indices ] = True
732+ # Create column mask for requested fields
733+ col_mask = torch .zeros (self .allocated_fields_num , dtype = torch .bool )
734+ field_indices = [self .field_name_mapping [field ] for field in field_names ]
735+ if field_indices :
736+ col_mask [field_indices ] = True
747737
748- # Filter production status by masks
749- relevant_status = self .production_status [row_mask ][:, col_mask ]
738+ # Filter production status by masks
739+ relevant_status = self .production_status [row_mask ][:, col_mask ]
750740
751741 # Check if all required fields are ready for each sample
752742 all_fields_ready = torch .all (relevant_status , dim = 1 )
@@ -878,32 +868,20 @@ def to_snapshot(self):
878868 Get a snapshot of partition status information.
879869
880870 Returns:
881- DataPartitionStatus object without threading.Lock()
871+ DataPartitionStatus object
882872 """
883873
884- def _perform_copy ():
885- cls = self .__class__
886- snapshot = cls .__new__ (cls )
887-
888- for name , value in self .__dict__ .items ():
889- if name == "data_status_lock" :
890- continue
891-
892- if isinstance (value , Tensor ):
893- new_val = value .clone ().detach ()
894- else :
895- new_val = copy .deepcopy (value )
874+ cls = self .__class__
875+ snapshot = cls .__new__ (cls )
896876
897- setattr (snapshot , name , new_val )
898- return snapshot
899-
900- lock_obj = getattr (self , "data_status_lock" , None )
877+ for name , value in self .__dict__ .items ():
878+ if isinstance (value , Tensor ):
879+ new_val = value .clone ().detach ()
880+ else :
881+ new_val = copy .deepcopy (value )
901882
902- if lock_obj :
903- with lock_obj :
904- return _perform_copy ()
905- else :
906- return _perform_copy ()
883+ setattr (snapshot , name , new_val )
884+ return snapshot
907885
908886 def clear_data (self , indexes_to_release : list [int ], clear_consumption : bool = True ):
909887 """Clear all production and optionally consumption data for given global_indexes."""
@@ -1021,7 +999,6 @@ def __init__(
1021999
10221000 # Start background processing threads
10231001 self ._start_process_handshake ()
1024- self ._start_process_update_data_status ()
10251002 self ._start_process_request ()
10261003
10271004 logger .info (f"TransferQueue Controller { self .controller_id } initialized" )
@@ -1070,7 +1047,7 @@ def _get_partition(self, partition_id: str) -> DataPartitionStatus | None:
10701047
10711048 def get_partition_snapshot (self , partition_id : str ) -> DataPartitionStatus | None :
10721049 """
1073- Get a copy of partition status information, without threading.Lock() .
1050+ Get a copy of partition status information.
10741051
10751052 Args:
10761053 partition_id: ID of the partition to retrieve
@@ -1623,8 +1600,7 @@ def kv_retrieve_meta(
16231600 partition .keys_mapping [keys [none_indexes [i ]]] = batch_global_indexes [i ]
16241601 partition .revert_keys_mapping [batch_global_indexes [i ]] = keys [none_indexes [i ]]
16251602
1626- with partition .data_status_lock :
1627- partition .ensure_samples_capacity (max (batch_global_indexes ) + 1 )
1603+ partition .ensure_samples_capacity (max (batch_global_indexes ) + 1 )
16281604
16291605 verified_global_indexes = [idx for idx in global_indexes if idx is not None ]
16301606 assert len (verified_global_indexes ) == len (keys )
@@ -1685,7 +1661,6 @@ def _init_zmq_socket(self):
16851661 try :
16861662 self ._handshake_socket_port = get_free_port (ip = self ._node_ip )
16871663 self ._request_handle_socket_port = get_free_port (ip = self ._node_ip )
1688- self ._data_status_update_socket_port = get_free_port (ip = self ._node_ip )
16891664
16901665 self .handshake_socket = create_zmq_socket (
16911666 ctx = self .zmq_context ,
@@ -1701,15 +1676,6 @@ def _init_zmq_socket(self):
17011676 )
17021677 self .request_handle_socket .bind (format_zmq_address (self ._node_ip , self ._request_handle_socket_port ))
17031678
1704- self .data_status_update_socket = create_zmq_socket (
1705- ctx = self .zmq_context ,
1706- socket_type = zmq .ROUTER ,
1707- ip = self ._node_ip ,
1708- )
1709- self .data_status_update_socket .bind (
1710- format_zmq_address (self ._node_ip , self ._data_status_update_socket_port )
1711- )
1712-
17131679 break
17141680 except zmq .ZMQError :
17151681 logger .warning (f"[{ self .controller_id } ]: Try to bind ZMQ sockets failed, retrying..." )
@@ -1722,7 +1688,6 @@ def _init_zmq_socket(self):
17221688 ports = {
17231689 "handshake_socket" : self ._handshake_socket_port ,
17241690 "request_handle_socket" : self ._request_handle_socket_port ,
1725- "data_status_update_socket" : self ._data_status_update_socket_port ,
17261691 },
17271692 )
17281693
@@ -1781,15 +1746,6 @@ def _start_process_handshake(self):
17811746 )
17821747 self .wait_connection_thread .start ()
17831748
1784- def _start_process_update_data_status (self ):
1785- """Start the data status update processing thread."""
1786- self .process_update_data_status_thread = Thread (
1787- target = self ._update_data_status ,
1788- name = "TransferQueueControllerProcessUpdateDataStatusThread" ,
1789- daemon = True ,
1790- )
1791- self .process_update_data_status_thread .start ()
1792-
17931749 def _start_process_request (self ):
17941750 """Start the request processing thread."""
17951751 self .process_request_thread = Thread (
@@ -1834,6 +1790,36 @@ def _process_request(self):
18341790 body = {"metadata" : metadata },
18351791 )
18361792
1793+ elif request_msg .request_type == ZMQRequestType .NOTIFY_DATA_UPDATE :
1794+ with monitor .measure (op_type = "NOTIFY_DATA_UPDATE" ):
1795+ message_data = request_msg .body
1796+ partition_id = message_data .get ("partition_id" )
1797+ global_indexes = message_data .get ("global_indexes" , [])
1798+
1799+ # Update production status
1800+ success = self .update_production_status (
1801+ partition_id = partition_id ,
1802+ global_indexes = global_indexes ,
1803+ field_schema = message_data .get ("field_schema" , {}),
1804+ custom_backend_meta = message_data .get ("custom_backend_meta" , {}),
1805+ )
1806+ if success :
1807+ if self ._metrics is not None :
1808+ self ._metrics .record_samples ("NOTIFY_DATA_UPDATE" , len (global_indexes ))
1809+ logger .debug (f"[{ self .controller_id } ]: Updated production status for partition { partition_id } " )
1810+
1811+ # Send acknowledgment
1812+ response_msg = ZMQMessage .create (
1813+ request_type = ZMQRequestType .NOTIFY_DATA_UPDATE_ACK ,
1814+ sender_id = self .controller_id ,
1815+ receiver_id = request_msg .sender_id ,
1816+ body = {
1817+ "controller_id" : self .controller_id ,
1818+ "partition_id" : partition_id ,
1819+ "success" : success ,
1820+ },
1821+ )
1822+
18371823 elif request_msg .request_type == ZMQRequestType .GET_PARTITION_META :
18381824 with monitor .measure (op_type = "GET_PARTITION_META" ):
18391825 params = request_msg .body
@@ -2047,50 +2033,6 @@ def _process_request(self):
20472033
20482034 self .request_handle_socket .send_multipart ([identity , * response_msg .serialize ()])
20492035
2050- def _update_data_status (self ):
2051- """Process data status update messages from storage units - adapted for partitions."""
2052- logger .debug (f"[{ self .controller_id } ]: start receiving update_data_status requests..." )
2053-
2054- perf_monitor = IntervalPerfMonitor (caller_name = self .controller_id )
2055-
2056- while True :
2057- monitor = self ._metrics if self ._metrics is not None else perf_monitor
2058-
2059- messages = self .data_status_update_socket .recv_multipart (copy = False )
2060- identity = messages .pop (0 )
2061- serialized_msg = messages
2062- request_msg = ZMQMessage .deserialize (serialized_msg )
2063-
2064- if request_msg .request_type == ZMQRequestType .NOTIFY_DATA_UPDATE :
2065- with monitor .measure (op_type = "NOTIFY_DATA_UPDATE" ):
2066- message_data = request_msg .body
2067- partition_id = message_data .get ("partition_id" )
2068- global_indexes = message_data .get ("global_indexes" , [])
2069-
2070- # Update production status
2071- success = self .update_production_status (
2072- partition_id = partition_id ,
2073- global_indexes = global_indexes ,
2074- field_schema = message_data .get ("field_schema" , {}),
2075- custom_backend_meta = message_data .get ("custom_backend_meta" , {}),
2076- )
2077- if success :
2078- if self ._metrics is not None :
2079- self ._metrics .record_samples ("NOTIFY_DATA_UPDATE" , len (global_indexes ))
2080- logger .debug (f"[{ self .controller_id } ]: Updated production status for partition { partition_id } " )
2081-
2082- # Send acknowledgment
2083- response_msg = ZMQMessage .create (
2084- request_type = ZMQRequestType .NOTIFY_DATA_UPDATE_ACK ,
2085- sender_id = self .controller_id ,
2086- body = {
2087- "controller_id" : self .controller_id ,
2088- "partition_id" : partition_id ,
2089- "success" : success ,
2090- },
2091- )
2092- self .data_status_update_socket .send_multipart ([identity , * response_msg .serialize ()])
2093-
20942036 def get_zmq_server_info (self ) -> ZMQServerInfo :
20952037 """Get ZMQ server connection information."""
20962038 return self .zmq_server_info
0 commit comments