class_docstr: Asyncio-based class that represents one DHT participant. Created via await DHTNode.create(...)
  Each DHTNode has an identifier, a local storage and access too other nodes via DHTProtocol.
  
  :note: Hivemind DHT is optimized to store a lot of temporary metadata that is regularly updated.
   For example, expert heartbeat emitted by a hivemind.moe.Server responsible for that expert.
   Such metadata does not require regular maintenance by peers or persistence on shutdown.
   Instead, DHTNode is designed to rapidly send bulk data and resolve conflicts.
  
  Every (key, value) pair in this DHT has an expiration time - float computed as get_dht_time() (UnixTime by default)
  DHT nodes always prefer values with higher expiration time and may delete any value past its expiration.
  
  Similar to Kademlia RPC protocol, hivemind DHT has 3 RPCs:
  
  * ping - request peer's identifier and update routing table (same as Kademlia PING RPC)
  * store - send several (key, value, expiration_time) pairs to the same peer (like Kademlia STORE, but in bulk)
  * find - request one or several keys, get values and expiration (if peer finds it locally) and :bucket_size: of
      nearest peers from recipient's routing table (ordered nearest-to-farthest, not including recipient itself)
      This RPC is a mixture between Kademlia FIND_NODE and FIND_VALUE with multiple keys per call.
  
  A DHTNode follows the following contract:
  
  - when asked to get(key), a node must find and return a value with highest expiration time that it found across DHT
    IF that time has not come yet. if expiration time is smaller than current get_dht_time(), node may return None;
  - when requested to store(key: value, expiration_time), a node must store (key => value) at until expiration time
    or until DHTNode gets the same key with greater expiration time. If a node is asked to store a key but it already
    has the same key with newer expiration, store will be rejected. Store returns True if accepted, False if rejected;
  - when requested to store(key: value, expiration_time, subkey=subkey), adds a sub-key to a dictionary value type.
    Dictionary values can have multiple sub-keys stored by different peers with individual expiration times. A subkey
    will be accepted to a dictionary either if there is no such sub-key or if new subkey's expiration is later than
    previous expiration under that subkey. See DHTProtocol.call_store for details.
  
  DHTNode also features several (optional) caching policies:
  
  - cache_locally: after GET, store the result in node's own local cache
  - cache_nearest: after GET, send the result to this many nearest nodes that don't have that value yet (see Kademlia)
  - cache_on_store: after STORE, either save or remove that key from node's own cache depending on store status
  - cache_refresh_before_expiry: if a value in cache was used and is about to expire, try to GET it this many seconds
    before expiration. The motivation here is that some frequent keys should be always kept in cache to avoid latency.
  - reuse_get_requests: if there are several concurrent GET requests, when one request finishes, DHTNode will attempt
    to reuse the result of this GET request for other requests with the same key. Useful for batch-parallel requests.
class_docstr_tok: ["Asyncio-based","class","that","represents","one","DHT","participant",".","Created","via","await","DHTNode.create","(","...",")","Each","DHTNode","has","an","identifier",",","a","local","storage","and","access","too","other","nodes","via","DHTProtocol",".",":","note",":","Hivemind","DHT","is","optimized","to","store","a","lot","of","temporary","metadata","that","is","regularly","updated",".","For","example",",","expert","heartbeat","emitted","by","a","hivemind.moe.Server","responsible","for","that","expert",".","Such","metadata","does","not","require","regular","maintenance","by","peers","or","persistence","on","shutdown",".","Instead",",","DHTNode","is","designed","to","rapidly","send","bulk","data","and","resolve","conflicts",".","Every","(","key",",","value",")","pair","in","this","DHT","has","an","expiration","time","-","float","computed","as","get_dht_time","(",")","(","UnixTime","by","default",")","DHT","nodes","always","prefer","values","with","higher","expiration","time","and","may","delete","any","value","past","its","expiration",".","Similar","to","Kademlia","RPC","protocol",",","hivemind","DHT","has","3","RPCs",":","*","ping","-","request","peer","'s","identifier","and","update","routing","table","(","same","as","Kademlia","PING","RPC",")","*","store","-","send","several","(","key",",","value",",","expiration_time",")","pairs","to","the","same","peer","(","like","Kademlia","STORE",",","but","in","bulk",")","*","find","-","request","one","or","several","keys",",","get","values","and","expiration","(","if","peer","finds","it","locally",")","and",":","bucket_size",":","of","nearest","peers","from","recipient","'s","routing","table","(","ordered","nearest-to-farthest",",","not","including","recipient","itself",")","This","RPC","is","a","mixture","between","Kademlia","FIND_NODE","and","FIND_VALUE","with","multiple","keys","per","call",".","A","DHTNode","follows","the","following","contract",":","-","when","asked","to","get","(","key",")",",","a","node","must","find","and","return","a","value","with","highest","expiration","time","that","it","found","across","DHT","IF","that","time","has","not","come","yet",".","if","expiration","time","is","smaller","than","current","get_dht_time","(",")",",","node","may","return","None",";","-","when","requested","to","store","(","key",":","value",",","expiration_time",")",",","a","node","must","store","(","key","=",">","value",")","at","until","expiration","time","or","until","DHTNode","gets","the","same","key","with","greater","expiration","time",".","If","a","node","is","asked","to","store","a","key","but","it","already","has","the","same","key","with","newer","expiration",",","store","will","be","rejected",".","Store","returns","True","if","accepted",",","False","if","rejected",";","-","when","requested","to","store","(","key",":","value",",","expiration_time",",","subkey=subkey",")",",","adds","a","sub-key","to","a","dictionary","value","type",".","Dictionary","values","can","have","multiple","sub-keys","stored","by","different","peers","with","individual","expiration","times",".","A","subkey","will","be","accepted","to","a","dictionary","either","if","there","is","no","such","sub-key","or","if","new","subkey","'s","expiration","is","later","than","previous","expiration","under","that","subkey",".","See","DHTProtocol.call_store","for","details",".","DHTNode","also","features","several","(","optional",")","caching","policies",":","-","cache_locally",":","after","GET",",","store","the","result","in","node","'s","own","local","cache","-","cache_nearest",":","after","GET",",","send","the","result","to","this","many","nearest","nodes","that","do","n't","have","that","value","yet","(","see","Kademlia",")","-","cache_on_store",":","after","STORE",",","either","save","or","remove","that","key","from","node","'s","own","cache","depending","on","store","status","-","cache_refresh_before_expiry",":","if","a","value","in","cache","was","used","and","is","about","to","expire",",","try","to","GET","it","this","many","seconds","before","expiration",".","The","motivation","here","is","that","some","frequent","keys","should","be","always","kept","in","cache","to","avoid","latency",".","-","reuse_get_requests",":","if","there","are","several","concurrent","GET","requests",",","when","one","request","finishes",",","DHTNode","will","attempt","to","reuse","the","result","of","this","GET","request","for","other","requests","with","the","same","key",".","Useful","for","batch-parallel","requests","."]
docstr: Called after get request is finished (whether it was found, not found, hit cache, cancelled, or reused)
docstr_tok: ["Called","after","get","request","is","finished","(","whether","it","was","found",",","not","found",",","hit","cache",",","cancelled",",","or","reused",")"]
code: def _trigger_cache_refresh(self, search: _SearchState):
      """Called after get request is finished (whether it was found, not found, hit cache, cancelled, or reused)"""
      if (
          search.found_something
          and search.source_node_id == self.node_id
      ):
          if (
              self.cache_refresh_before_expiry
              and search.key_id in self.protocol.cache
          ):
              self._schedule_for_refresh(
                  search.key_id,
                  search.expiration_time
                  - self.cache_refresh_before_expiry,
              )
  
imports: from __future__ import annotations
  import asyncio
  import dataclasses
  import os
  import random
  from collections import Counter, defaultdict
  from dataclasses import dataclass, field
  from functools import partial
  from typing import Any, Awaitable, Callable, Collection, DefaultDict, Dict, List, Optional, Sequence, Set, Tuple, Type, Union
  from multiaddr import Multiaddr
  from sortedcontainers import SortedSet
  from hivemind.dht.crypto import DHTRecord, RecordValidatorBase
  from hivemind.dht.protocol import DHTProtocol
  from hivemind.dht.routing import DHTID, BinaryDHTValue, DHTKey, DHTValue, Subkey, get_dht_time
  from hivemind.dht.storage import DictionaryDHTValue
  from hivemind.dht.traverse import traverse_dht
  from hivemind.p2p import P2P, PeerID
  from hivemind.utils import MSGPackSerializer, SerializerBase, get_logger
  from hivemind.utils.auth import AuthorizerBase
  from hivemind.utils.timed_storage import DHTExpiration, TimedStorage, ValueWithExpiration
label_desc: Use image node_id 5 for calling the DHTNode obj's underlying member method code with example usage: obj._trigger_cache_refresh(search) without return types