async def call_store( self, peer: Endpoint, keys: Sequence[DHTID], values: Sequence[BinaryDHTValue], expiration_time: Union[DHTExpiration, Sequence[DHTExpiration]], in_cache: Optional[Union[bool, Sequence[bool]]] = None) -> Sequence[bool]: """ Ask a recipient to store several (key, value : expiration_time) items or update their older value :param peer: request this peer to store the data :param keys: a list of N keys digested by DHTID.generate(source=some_dict_key) :param values: a list of N serialized values (bytes) for each respective key :param expiration_time: a list of N expiration timestamps for each respective key-value pair (see get_dht_time()) :param in_cache: a list of booleans, True = store i-th key in cache, value = store i-th key locally :note: the difference between storing normally and in cache is that normal storage is guaranteed to be stored until expiration time (best-effort), whereas cached storage can be evicted early due to limited cache size :return: list of [True / False] True = stored, False = failed (found newer value or no response) if peer did not respond (e.g. due to timeout or congestion), returns None """ if isinstance(expiration_time, DHTExpiration): expiration_time = [expiration_time] * len(keys) in_cache = in_cache if in_cache is not None else [False] * len( keys) # default value (None) in_cache = [in_cache] * len(keys) if isinstance( in_cache, bool) else in_cache # single bool keys, values, expiration_time, in_cache = map( list, [keys, values, expiration_time, in_cache]) assert len(keys) == len(values) == len(expiration_time) == len( in_cache), "Data is not aligned" store_request = dht_pb2.StoreRequest(keys=list( map(DHTID.to_bytes, keys)), values=values, expiration_time=expiration_time, in_cache=in_cache, peer=self.node_info) try: async with self.rpc_semaphore: response = await self._get(peer).rpc_store( store_request, timeout=self.wait_timeout) if response.peer and response.peer.node_id: peer_id = DHTID.from_bytes(response.peer.node_id) asyncio.create_task( self.update_routing_table(peer_id, peer, responded=True)) return response.store_ok except grpc.experimental.aio.AioRpcError as error: logger.warning( f"DHTProtocol failed to store at {peer}: {error.code()}") asyncio.create_task( self.update_routing_table( self.routing_table.get(endpoint=peer), peer, responded=False)) return [False] * len(keys)
async def call_store(self, peer: Endpoint, keys: Sequence[DHTID], values: Sequence[Union[BinaryDHTValue, DictionaryDHTValue]], expiration_time: Union[DHTExpiration, Sequence[DHTExpiration]], subkeys: Optional[Union[Subkey, Sequence[Optional[Subkey]]]] = None, in_cache: Optional[Union[bool, Sequence[bool]]] = None) -> Optional[List[bool]]: """ Ask a recipient to store several (key, value : expiration_time) items or update their older value :param peer: request this peer to store the data :param keys: a list of N keys digested by DHTID.generate(source=some_dict_key) :param values: a list of N serialized values (bytes) for each respective key :param expiration_time: a list of N expiration timestamps for each respective key-value pair(see get_dht_time()) :param subkeys: a list of N optional sub-keys. If None, stores value normally. If not subkey is not None: 1) if local storage doesn't have :key:, create a new dictionary {subkey: (value, expiration_time)} 2) if local storage already has a dictionary under :key:, try add (subkey, value, exp_time) to that dictionary 2) if local storage associates :key: with a normal value with smaller expiration, clear :key: and perform (1) 3) finally, if local storage currently associates :key: with a normal value with larger expiration, do nothing :param in_cache: a list of booleans, True = store i-th key in cache, value = store i-th key locally :note: the difference between storing normally and in cache is that normal storage is guaranteed to be stored until expiration time (best-effort), whereas cached storage can be evicted early due to limited cache size :return: list of [True / False] True = stored, False = failed (found newer value or no response) if peer did not respond (e.g. due to timeout or congestion), returns None """ if isinstance(expiration_time, DHTExpiration): expiration_time = [expiration_time] * len(keys) if subkeys is None: subkeys = [None] * len(keys) in_cache = in_cache if in_cache is not None else [False] * len(keys) # default value (None) in_cache = [in_cache] * len(keys) if isinstance(in_cache, bool) else in_cache # single bool keys, subkeys, values, expiration_time, in_cache = map(list, [keys, subkeys, values, expiration_time, in_cache]) for i in range(len(keys)): if subkeys[i] is None: # add default sub-key if not specified subkeys[i] = self.IS_DICTIONARY if isinstance(values[i], DictionaryDHTValue) else self.IS_REGULAR_VALUE else: subkeys[i] = self.serializer.dumps(subkeys[i]) if isinstance(values[i], DictionaryDHTValue): assert subkeys[i] == self.IS_DICTIONARY, "Please don't specify subkey when storing an entire dictionary" values[i] = self.serializer.dumps(values[i]) assert len(keys) == len(values) == len(expiration_time) == len(in_cache), "Data is not aligned" store_request = dht_pb2.StoreRequest(keys=list(map(DHTID.to_bytes, keys)), subkeys=subkeys, values=values, expiration_time=expiration_time, in_cache=in_cache, peer=self.node_info) try: async with self.rpc_semaphore: response = await self._get_dht_stub(peer).rpc_store(store_request, timeout=self.wait_timeout) if response.peer and response.peer.node_id: peer_id = DHTID.from_bytes(response.peer.node_id) asyncio.create_task(self.update_routing_table(peer_id, peer, responded=True)) return response.store_ok except grpc.aio.AioRpcError as error: logger.debug(f"DHTProtocol failed to store at {peer}: {error.code()}") asyncio.create_task(self.update_routing_table(self.routing_table.get(endpoint=peer), peer, responded=False)) return None