2323
2424from transfer_queue .storage .clients .base import TransferQueueStorageKVClient
2525from transfer_queue .storage .clients .factory import StorageClientFactory
26+ from transfer_queue .utils .tensor_utils import allocate_empty_tensors , get_nbytes , merge_continues_memory
2627
2728logger = logging .getLogger (__name__ )
2829logger .setLevel (os .getenv ("TQ_LOGGING_LEVEL" , logging .WARNING ))
2930
3031MOONCAKE_STORE_IMPORTED : bool = True
3132try :
32- from mooncake .store import MooncakeDistributedStore
33+ from mooncake .store import MooncakeDistributedStore , ReplicateConfig
34+
3335except ImportError :
3436 MOONCAKE_STORE_IMPORTED = False
3537
@@ -78,10 +80,9 @@ def __init__(self, config: dict[str, Any]):
7880 if not self .metadata_server .startswith ("etcd://" ) and not self .metadata_server .endswith ("/metadata" ):
7981 self .metadata_server = self .metadata_server + "/metadata"
8082
81- if self .metadata_server is None :
82- raise ValueError ("Missing 'metadata_server' in config" )
83- if self .master_server_address is None :
84- raise ValueError ("Missing 'master_server_address' in config" )
83+ self .replica_config = ReplicateConfig ()
84+ # FIXME: hard_pin is not supported yet
85+ # self.replica_config.with_hard_pin = True
8586
8687 self ._store = MooncakeDistributedStore ()
8788 ret = self ._store .setup (
@@ -116,12 +117,8 @@ def put(self, keys: list[str], values: list[Any]) -> Optional[list[Any]]:
116117
117118 for key , value in zip (keys , values , strict = True ):
118119 if isinstance (value , torch .Tensor ):
119- tensor = value .contiguous ()
120- # TODO: use gpu direct rdma instead
121- if tensor .device .type == "cuda" :
122- tensor = tensor .cpu ()
123120 tensor_keys .append (key )
124- tensor_values .append (tensor )
121+ tensor_values .append (value )
125122 else :
126123 non_tensor_keys .append (key )
127124 non_tensor_values .append (pickle .dumps (value ))
@@ -139,38 +136,50 @@ def _batch_put_tensors(self, keys: list[str], tensors: list[Tensor]):
139136 batch_keys = keys [i : i + BATCH_SIZE_LIMIT ]
140137 batch_tensors = tensors [i : i + BATCH_SIZE_LIMIT ]
141138
142- results = self ._store .batch_put_tensor (batch_keys , batch_tensors )
139+ batch_ptrs , batch_sizes = self ._preprocess_tensors_for_put (batch_tensors )
140+ batch_ptr_reduced , batch_sizes_reduced = merge_continues_memory (batch_ptrs , batch_sizes )
141+ self ._register_all_buffers (batch_ptr_reduced , batch_sizes_reduced )
142+
143+ results = self ._store .batch_upsert_from (batch_keys , batch_ptrs , batch_sizes , config = self .replica_config )
143144 if not all (r == 0 for r in results ):
144145 failed_indices = [j for j , r in enumerate (results ) if r != 0 ]
145146 error_codes = [results [j ] for j in failed_indices ]
146147 raise RuntimeError (
147148 f"batch_put_tensor failed for indices { failed_indices } with error codes: { error_codes } "
148149 )
149150
151+ self ._unregister_all_buffers (batch_ptr_reduced )
152+
150153 def _batch_put_bytes (self , keys : list [str ], values : list [bytes ]):
151154 for i in range (0 , len (keys ), BATCH_SIZE_LIMIT ):
152155 batch_keys = keys [i : i + BATCH_SIZE_LIMIT ]
153156 batch_values = values [i : i + BATCH_SIZE_LIMIT ]
154157
155- ret = self ._store .put_batch (batch_keys , batch_values )
158+ ret = self ._store .upsert_batch (batch_keys , batch_values , self . replica_config )
156159 if ret != 0 :
157160 raise RuntimeError (f"put_batch failed with error code: { ret } " )
158161
159- def get (self , keys : list [str ], shapes = None , dtypes = None , custom_backend_meta = None ) -> list [Any ]:
162+ def get (
163+ self ,
164+ keys : list [str ],
165+ shapes : Optional [list [Any ]] = None ,
166+ dtypes : Optional [list [Any ]] = None ,
167+ custom_backend_meta : Optional [list [str ]] = None ,
168+ ) -> list [Any ]:
160169 """Get multiple key-value pairs from MooncakeStore.
161170
162171 Args:
163- keys (List[str]) : Keys to fetch.
164- shapes (List[List[int]]) : Expected tensor shapes (use [] for scalars).
165- dtypes (List[Optional[torch.dtype]]) : Expected dtypes; use None for non-tensor data.
166- custom_backend_meta (List[str], optional): .. .
172+ keys: Keys to fetch.
173+ shapes: Expected tensor shapes (use [] for scalars).
174+ dtypes: Expected dtypes; use None for non-tensor data.
175+ custom_backend_meta: Optional custom backend metadata .
167176
168177 Returns:
169- List[Any]: Retrieved values in the same order as input keys.
178+ Retrieved values in the same order as input keys.
170179 """
171180
172181 if shapes is None or dtypes is None :
173- raise ValueError ("MooncakeStoreClient needs shapes and dtypes" )
182+ raise ValueError ("MooncakeStoreClient needs shapes and dtypes for zero-copy transfer. " )
174183 if not (len (keys ) == len (shapes ) == len (dtypes )):
175184 raise ValueError ("Lengths of keys, shapes, dtypes must match" )
176185
@@ -210,14 +219,25 @@ def _batch_get_tensors(self, keys: list[str], shapes: list, dtypes: list) -> lis
210219 batch_shapes = shapes [i : i + BATCH_SIZE_LIMIT ]
211220 batch_dtypes = dtypes [i : i + BATCH_SIZE_LIMIT ]
212221
213- batch_results = self ._store .batch_get_tensor (batch_keys )
222+ batch_nbytes = get_nbytes (batch_dtypes , batch_shapes )
223+ batch_buffer_tensors , batch_buffer_ptrs = allocate_empty_tensors (batch_dtypes , batch_shapes )
214224
215- if len (batch_results ) != len (batch_keys ):
216- raise RuntimeError (f"batch_get_tensor returned { len (batch_results )} items, expected { len (batch_keys )} " )
225+ batch_ptrs = batch_buffer_ptrs
217226
218- for j , (tensor , shape , dtype ) in enumerate (zip (batch_results , batch_shapes , batch_dtypes , strict = True )):
219- if tensor is None :
220- raise RuntimeError (f"batch_get_tensor returned None for key '{ batch_keys [j ]} '" )
227+ self ._register_all_buffers (batch_ptrs , batch_nbytes )
228+ ret_codes = self ._store .batch_get_into (batch_keys , batch_ptrs , batch_nbytes )
229+ self ._unregister_all_buffers (batch_ptrs )
230+
231+ if len (ret_codes ) != len (batch_keys ):
232+ raise RuntimeError (f"batch_get_into returned { len (ret_codes )} results, expected { len (batch_keys )} " )
233+
234+ # Check result codes and validate tensors
235+ # Note: Positive values indicate success (bytes read), negative values indicate error
236+ for j , (tensor , shape , dtype , ret_code ) in enumerate (
237+ zip (batch_buffer_tensors , batch_shapes , batch_dtypes , ret_codes , strict = True )
238+ ):
239+ if ret_code < 0 :
240+ raise RuntimeError (f"batch_get_into failed for key '{ batch_keys [j ]} ' with error code: { ret_code } " )
221241 if tensor .shape != torch .Size (shape ):
222242 raise RuntimeError (
223243 f"Shape mismatch for key '{ batch_keys [j ]} ': expected { shape } , got { tensor .shape } "
@@ -243,26 +263,35 @@ def _batch_get_bytes(self, keys: list[str]) -> list[bytes]:
243263 def clear (self , keys : list [str ], custom_backend_meta = None ):
244264 """Deletes multiple keys from MooncakeStore.
245265
246-
247266 Args:
248267 keys (List[str]): List of keys to remove.
249268 custom_backend_meta (List[Any], optional): ...
250269 """
251- global_indexes_patterns = {key .split ("@" )[0 ] + "@.*" for key in keys }
252- for p in global_indexes_patterns :
253- ret = self ._store .remove_by_regex (p , force = True )
254- if ret < 0 :
255- logger .warning (f"remove failed for key '{ p } ' with error code: { ret } " )
256-
257- # FIXME: controller returned BatchMeta may have mismatched fields in some case, preventing
258- # key-value based backends to accurately clear all existing keys..
259- # for key in keys:
260- # ret = self._store.remove(key)
261- # if not (ret == 0 or ret == -704):
262- # logger.warning(f"remove failed for key '{key}' with error code: {ret}")
270+ rets = self ._store .batch_remove (keys , force = True )
271+ for i , ret in enumerate (rets ):
272+ if not (ret == 0 or ret == - 704 ):
273+ logger .error (f"remove failed for key '{ keys [i ]} ' with error code: { ret } " )
263274
264275 def close (self ):
265276 """Closes MooncakeStore."""
266277 if self ._store :
267278 self ._store .close ()
268279 self ._store = None
280+
281+ @staticmethod
282+ def _preprocess_tensors_for_put (values : list [Tensor ]) -> tuple [list [Any ], list [Any ]]:
283+ ptr_list = []
284+ size_list = []
285+ for t in values :
286+ t = t .contiguous ()
287+ ptr_list .append (t .data_ptr ())
288+ size_list .append (t .nbytes )
289+ return ptr_list , size_list
290+
291+ def _register_all_buffers (self , ptrs , sizes ):
292+ for ptr , size in zip (ptrs , sizes , strict = False ):
293+ self ._store .register_buffer (ptr , size )
294+
295+ def _unregister_all_buffers (self , ptrs ):
296+ for ptr in ptrs :
297+ self ._store .unregister_buffer (ptr )
0 commit comments