Differences between revisions 1 and 40 (spanning 39 versions)
Revision 1 as of 2015-02-06 17:57:48
Size: 6249
Editor: lukisi
Comment:
Revision 40 as of 2015-10-15 10:09:26
Size: 43430
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
= Modulo PeerServices - Appunti - Algoritmi principali =
=== Algoritmo di rilevamento non partecipazione ===
==== bool check_non_participation(p_id, lvl, pos) ====
 * In questo algoritmo non ci interessa sapere se un g-nodo partecipa, ma solo se è possibile dire con certezza che esso non partecipa. Se è incerto restituisce False, esattamente come farebbe se potesse dire con certezza che partecipa.
 * Produci ''x̄'' = la tupla x̄~-,,0,,-~·x̄~-,,1,,-~·...·x̄~-,,lvl-1,,-~ dove x̄~-,,i,,-~ = 0 per ogni i. La tupla identifica un indirizzo a caso all'interno del g-nodo ''g'' = (lvl, pos). Se lvl = 0 allora x̄ è null.
 * Produci ''n'' = la tupla n~-,,0,,-~·n~-,,1,,-~·...·n~-,,lvl,,-~. La tupla che identifica il nodo corrente nel g-nodo di livello lvl+1 in cui il messaggio si muoverà.
= Modulo PeerServices - Appunti - Algoritmi 2 / 2 =
<<TableOfContents(4)>>

== Algoritmo di rilevamento di non partecipazione ==
=== check_non_participation ===
'''bool check_non_participation
(p_id, lvl, _pos)'''
 * In questo algoritmo non ci interessa sapere se un g-nodo partecipa, ma solo se è possibile dire con certezza che esso non partecipa. In caso di incertezza l'algoritmo restituisce False.
 * Produci ''x̄'' = la tupla x̄~-,,0,,-~·x̄~-,,1,,-~·...·x̄~-,,lvl-1,,-~ dove x̄~-,,i,,-~ = 0 per ogni i. La tupla identifica un indirizzo a caso all'interno del g-nodo ''g'' = (lvl, _pos). Se lvl = 0 allora x̄ è null.
 * Produci ''n'' = make_tuple_node(new HCoord(0, pos[0]), lvl+1) , cioè la tupla n~-,,0,,-~·n~-,,1,,-~·...·n~-,,lvl,,-~. La tupla che identifica il nodo corrente nel g-nodo di livello lvl+1 in cui il messaggio si muoverà.
Line 11: Line 14:
 * ''m’.pos'' = pos.  * ''m’.pos'' = _pos.
Line 15: Line 18:
 * Prepara ''waiting_answer'' = new !WaitingAnswer(null, (lvl,pos) come PeerTupleGNode con top = lvl+1). ~-Il fatto che l'istanza di !RemoteCall è a null fa in modo che i metodi remoti che ricevono le notifiche si comportano in modo adeguato. Sostanzialmente dovrebbe cambiare solo il fatto che quando si riceve la segnalazione di get_request si risponde sempre con l'eccezione !PeersUnknownMessageError, ache se si è potuto recuperare l'istanza di !WaitingAnswer. Sull'istanza di !WaitingAnswer viene poi valorizzato il membro response con un !SerializableNone solo per indicare che il g-nodo partecipa.-~  * Prepara ''waiting_answer'' = new !WaitingAnswer(null, (lvl,_pos) come PeerTupleGNode con top = lvl+1). ~-Il fatto che l'istanza di IPeersRequest è a null fa in modo che i metodi remoti che ricevono le notifiche si comportano in modo adeguato. Sostanzialmente dovrebbe cambiare solo il fatto che quando si riceve la segnalazione di get_request si risponde sempre con l'eccezione !PeersUnknownMessageError, anche se si è potuto recuperare l'istanza di !WaitingAnswer. Sull'istanza di !WaitingAnswer viene poi valorizzato il membro response con qualcosa diverso da ''null'' solo per indicare che il g-nodo partecipa.-~
Line 17: Line 20:
 * IAddressManagerRootDispatcher ''gwstub''
 * IAddressManagerRootDispatcher? ''failed'' = null
 * IPeersManagerStub ''gwstub''
 * IPeersManagerStub? ''failed'' = null
Line 21: Line 24:
   * Calcola gwstub = map_paths.i_peers_gateway(lvl, pos, null, failed)    * Calcola gwstub = map_paths.i_peers_gateway(lvl, _pos, null, failed)
Line 25: Line 28:
   * Esegue gwstub.peers_manager.forward_peer_message(m’).
  * Se riceve l'eccezione RPCError:
   * Esegue gwstub.forward_peer_message(m’).
  * Se riceve !StubError o !DeserializeError:
Line 42: Line 45:
   * # significa che abbiamo ricevuto il contatto e che lvl,pos partecipa.    * # significa che abbiamo ricevuto il contatto e che lvl,_pos partecipa.
Line 51: Line 54:
=== Algoritmo di divulgazione della partecipazione ===
==== void publish_my_participation(p_id) ====
== Algoritmo di divulgazione della partecipazione ==
=== publish_my_participation ===
'''
void publish_my_participation(p_id)'''
Line 64: Line 68:
    * tcp_stub.peer_manager.set_participant(p_id, gn).
   * Se riceve eccezione RPCError: ignora.
    * tcp_stub.set_participant(p_id, gn).
   * Se riceve !StubError o !DeserializeError:
   * Ignora.
Line 68: Line 73:
   * br_stub.peer_manager.set_participant(p_id, gn).
  * Se riceve eccezione RPCError: ignora.
   * br_stub.set_participant(p_id, gn).
  * Se riceve !StubError o !DeserializeError:
  * Ignora.
Line 72: Line 78:
==== void set_participant(int p_id, PeerTupleGNode gn) ==== === set_participant ===
'''
void set_participant(int p_id, PeerTupleGNode gn)'''
Line 83: Line 90:
  * Se lista_recenti.contains(ret):   * Se ret ∈ lista_recenti:
Line 94: Line 101:
     * tcp_stub.peer_manager.set_participant(p_id, ret_gn).
    * Se riceve eccezione RPCError: ignora.
     * tcp_stub.set_participant(p_id, ret_gn).
    * Se riceve !StubError o !DeserializeError:
    * Ignora.
Line 98: Line 106:
    * br_stub.peer_manager.set_participant(p_id, ret_gn).
   * Se riceve eccezione RPCError: ignora.
    * br_stub.set_participant(p_id, ret_gn).
   * Se riceve !StubError o !DeserializeError:
   * Ignora.
Line 103: Line 112:

== Algoritmi per il mantenimento di un database distribuito ==
=== per assicurare delle repliche ===
'''bool begin_replica(q, p_id, x̄, r, timeout_exec, out IPeersResponse? resp, out IPeersContinuation cont)'''
 * Gli argomenti sono:
  * int ''q'': il numero delle repliche richieste,
  * int ''p_id'',
  * !PeerTupleNode ''x̄'': la tupla dell'hash della chiave del record, cioè h~-,,p,,-~ ( k ),
  * IPeersRequest ''r'': la richiesta di replicare la coppia k,val ,
  * int ''timeout_exec'',
  * ''resp'' viene valorizzato con la risposta o null;
  * ''cont'' è un oggetto di cui all'esterno si sa solo che implementa l'interfaccia vuota IPeersContinuation.
 * ''lista_repliche'' = new List di !PeerTupleNode.
 * ''exclude_tuple_list'' = new PeerTupleGNodeContainer(x̄.tuple.size).
 * cont = {q, p_id, x̄, r, timeout_exec, lista_repliche, exclude_tuple_list}.
 * Return next_replica(cont, out resp).

'''bool next_replica(IPeersContinuation cont, out IPeersResponse? resp)'''
 * resp = null.
 * Se cont.lista_repliche.size ≥ cont.q:
  * Return False.
 * !PeerTupleNode ''respondant'';
 * ret = contact_peer(cont.p_id, cont.x̄, cont.r, cont.timeout_exec, True, out respondant, cont.exclude_tuple_list).
 * Se si riceve l'eccezione !PeersNoParticipantsInNetworkError o !PeersRefuseExecutionError:
  * Return False.
 * resp = ret.
 * aggiungi respondant a cont.lista_repliche.
 * aggiungi respondant a cont.exclude_tuple_list.
 * Return cont.lista_repliche.size < cont.q.

=== per reperire i record di pertinenza nella fase di ingresso in una rete ===
Il modulo fornisce alcuni algoritmi alle classi che implementano servizi che prevedono il mantenimento di database distribuiti. Con questi algoritmi tali classi sono facilitate nel compito di reperire i record di pertinenza nella fase di ingresso in una rete. Questi algoritmi pongono alcune regole che la classe del servizio deve rispettare:
 * Deve essere possibile rappresentare la chiave di un record con una istanza di una classe derivata da ''Object'' che sia serializzabile.
 * Deve essere possibile rappresentare il contenuto di un record con una istanza di una classe derivata da ''Object'' che sia serializzabile. Tale classe non deve necessariamente contenere anche la chiave.
 * Non viene imposto nessun obbligo riguardo il formato delle classi che rappresentano le richieste del servizio. La classe del servizio deve essere in grado di costruire una istanza della chiave interessata partendo dalla istanza che rappresenta la richiesta. Deve essere inoltre in grado di stabilire se la richiesta è di sola lettura, di sola scrittura, o di lettura e successiva scrittura.
 * Non viene imposto nessun obbligo riguardo il formato delle classi che rappresentano le risposte del servizio. La classe del servizio deve essere in grado di costruire una risposta che indica l'eccezione ''NOT_FOUND'' per una certa chiave.

Il modulo fornisce la classe !RequestWaitThenSendRecord. Si tratta di una classe serializzabile, che deriva da Object, e contiene una istanza di Object serializzabile ''k''. Implementa l'interfaccia (vuota) IPeersRequest. È la richiesta di aspettare un tempo ''δ'' (valutato direttamente dal nodo che riceve la richiesta) e poi inviare il record relativo alla chiave ''k''. Tale classe viene esposta dal modulo, in quanto la classe del servizio deve poter riconoscere una sua istanza, ma i suoi membri non sono esposti, incluso il costruttore.

Il modulo fornisce la classe !RequestWaitThenSendRecordResponse. Si tratta di una classe serializzabile, che deriva da Object, e contiene una istanza di Object serializzabile ''record''. Implementa l'interfaccia (vuota) IPeersResponse. È la risposta alla richiesta sopra descritta. Tale classe non viene esposta dal modulo.

Il modulo fornisce la classe !RequestWaitThenSendRecordNotFound. Si tratta di una classe serializzabile, che deriva da Object, e non ha alcun dato al suo interno. Implementa l'interfaccia (vuota) IPeersResponse. È la risposta come eccezione ''NOT_FOUND'' alla richiesta sopra descritta. Tale classe non viene esposta dal modulo.

==== servizi con scadenza temporale dei record ====
I metodi ''ttl_db_*'' servono a gestire un database distribuito in cui i record hanno una scadenza, o TTL. Il modulo, oltre a fornire tali metodi, fornisce la classe !RequestSendKeys, la classe !RequestSendKeysResponse, la classe !TemporalDatabaseHandler, l'interfaccia ITemporalDatabaseDescriptor.

La classe !RequestSendKeys è una classe serializzabile, che deriva da Object, e non ha alcun dato al suo interno. Implementa l'interfaccia (vuota) IPeersRequest. È la richiesta di inviare tutte le chiavi memorizzate. Tale classe viene esposta dal modulo, in quanto la classe del servizio deve poter riconoscere una sua istanza, ma i suoi membri non sono esposti, incluso il costruttore.

La classe !RequestSendKeysResponse è una classe serializzabile, che deriva da Object, e contiene una lista di istanze di Object serializzabili. Implementa l'interfaccia (vuota) IPeersResponse. È la risposta alla richiesta di inviare tutte le chiavi memorizzate. Tale classe non viene esposta dal modulo.

La classe !TemporalDatabaseHandler è esposta dal modulo, ma il suo contenuto è del tutto oscuro all'esterno del modulo. I suoi membri, accessibili solo dal modulo, sono:
 * ''internal !TemporalDatabaseHandler'': lo stesso costruttore della classe è accessibile solo dal modulo.
 * ''internal Timer timer_non_exhaustive'': un timer. Se è ''null'' o è scaduto significa che il nodo si considera ''esaustivo'' per i record di sua pertinenza, altrimenti no.
 * ''internal List<Object> removed_keys'': una lista di chiavi che sono state cancellate su richiesta, per le quali il nodo può rispondere NOT_FOUND anche se non è ''esaustivo''.
 * ''internal !HashMap<Object,INtkdChannel> retrieving_keys'': una mappa che associa ad una chiave un canale di comunicazione tra tasklet se per tale chiave è in corso una operazione di recupero.

L'interfaccia ITemporalDatabaseDescriptor espone questi metodi:
 * ''!TemporalDatabaseHandler tdh'': una proprietà destinata ad essere popolata ed acceduta dal modulo con una istanza di !TemporalDatabaseHandler.
 * ''int p_id'': una proprietà che dice l'identificativo di questo servizio.
 * ''int timeout_exec_send_keys'': una proprietà che dice il tempo massimo di esecuzione per la richiesta !RequestSendKeys per questo servizio.
 * ''int timeout_exec_wait_then_send_record'': una proprietà che dice il tempo massimo di esecuzione per la richiesta !RequestWaitThenSendRecord per questo servizio.
 * ''int msec_ttl'': una proprietà che dice il numero di millisecondi prima che un record di questo servizio scada.
 * ''int max_records'': una proprietà che dice il numero massimo di record che il nodo può memorizzare per questo servizio.
 * ''bool is_valid_key(Object k)'': una funzione che dice se ''k'' è una chiave valida per il servizio.
 * ''List<int> evaluate_hash_node(Object k, int lvl)'': una funzione che calcola la tupla ''h~-,,p,,-~(k)'' di questo servizio per la chiave ''k'' tenendo in considerazione che servono solo le posizioni interne al livello ''lvl''.
 * ''bool key_equal_data(Object k1, Object k2)'' e ''uint key_hash_data(Object k)'': metodi le cui firme sono adatte per i delegati ''Gee.!EqualDataFunc<Object>'' e ''Gee.!HashDataFunc<Object>'', per costruire una !HashMap o una lista "con funzionalità di ricerca" di chiavi del servizio.
 * ''int my_records_size()'': una funzione per sapere il numero di record per questo servizio attualmente memorizzati dal nodo.
 * ''bool my_records_contains(Object k)'': una funzione per sapere se il record per la chiave ''k'' per questo servizio è attualmente memorizzato dal nodo.
 * ''List<Object> get_all_keys()'': una funzione che restituisce la lista di chiavi detenute in memoria.
 * ''Object get_key_from_request(IPeersRequest r)'': una funzione che partendo dalla richiesta ''r'', la quale non deve essere una istanza di !RequestSendKeys, né una istanza di !RequestWaitThenSendRecord, restituisce la chiave ''k'' a cui si riferisce.
 * ''bool is_read_only_request(IPeersRequest r)'': una funzione che dice se la richiesta è di sola lettura.
 * ''bool is_write_only_request(IPeersRequest r)'': una funzione che dice se la richiesta è di sola scrittura.
 * ''bool is_read_write_request(IPeersRequest r)'': una funzione che dice se la richiesta è di lettura e successiva scrittura. Per una richiesta ''r'' che non sia una istanza di !RequestSendKeys, né una istanza di !RequestWaitThenSendRecord, uno e uno solo di questi tre metodi restituisce ''True''.
 * ''IPeersResponse execute_request(IPeersRequest r)'': una funzione che elabora la richiesta ''r'', la quale non deve essere una istanza di !RequestSendKeys, né una istanza di !RequestWaitThenSendRecord, e produce una istanza di IPeersResponse che possa essere usata come valore di ritorno.
 * ''IPeersResponse prepare_response_not_found(IPeersRequest r, Object k)'': una funzione per produrre una istanza di IPeersResponse che possa essere usata come eccezione NOT_FOUND per la richiesta ''r'' e la chiave ''k''.
 * ''Object get_record_for_key(Object k)'': una funzione che restituisce il record per la chiave ''k'', avendo come requisito (cioè il chiamante deve averlo già verificato) che tale chiave è presente nella memoria.
 * ''void set_record_for_key(Object k, Object rec)'': una funzione che mette in memoria il record ''rec'' per la chiave ''k'', avendo come requisito (cioè il chiamante deve averlo già verificato) che la memoria non sia ancora esaurita. Questa funzione deve garantire di essere atomica, cioè di non schedulare altre tasklet.

'''void ttl_db_begin(ITemporalDatabaseDescriptor tdd, List<int> tuple_n)'''
 * Viene avviata in una tasklet dalla classe del servizio, che deriva !PeerService.
 . Se si tratta di un servizio opzionale, viene chiamata solo dopo che sono state reperite con successo le mappe dei partecipanti ai servizi opzionali.
 . Gli argomenti del metodo ''ttl_db_begin'' sono:
  * ''tdd'': istanza di una classe che implementa ITemporalDatabaseDescriptor sopra descritta.
  * ''tuple_n'': una tupla con tutte le posizioni del nodo da 0 a ''levels'' - 1.
 * ''tdd.tdh'' = new !TemporalDatabaseHandler().
 * ''tdd.tdh.timer_non_exhaustive'' = un nuovo timer che scade dopo ''tdd.msec_ttl'' millisecondi.
 * ''tdd.tdh.removed_keys'' = new !ArrayList<Object>(''tdd.key_equal_data'').
 * ''tdd.tdh.retrieving_keys'' = new !HashMap<Object,INtkdChannel>(''tdd.key_equal_data'', ''tdd.key_hash_data'').
 * ''IPeersRequest r'' = new !RequestSendKeys().
 * Try:
  * ''!PeerTupleNode respondant'' = null. Sarà una tupla nel g-nodo di livello ''levels'', poiché ''tuple_n'' ha ''levels'' elementi.
  * Esegue ''IPeersResponse ret = contact_peer(tdd.p_id, tuple_n, r, tdd.timeout_exec_send_keys, True, out respondant)''.
  . Il valore restituito dovrebbe essere un !RequestSendKeysResponse, cioè una lista di Object. Altrimenti la risposta viene ignorata.
  * Se ''ret'' è una istanza di !RequestSendKeysResponse:
   * Per ogni chiave ''k'' in ''ret'':
    * Se ''tdd.my_records_size()'' + ''tdd.tdh.retrieving_keys.size'' < ''tdd.max_records'':
     * Se ''tdd.is_valid_key(k)'':
      * Se '''not''' ''tdd.my_records_contains(k)'' '''e''' ''k'' ∉ ''tdd.tdh.removed_keys'' '''e''' '''not''' ''tdd.tdh.retrieving_keys.has_key(k)'':
       * # Non sa nulla di ''k''.
       * Se ''dist(tdd.evaluate_hash_node(k, tuple_n.size), tuple_n)'' < ''dist(tdd.evaluate_hash_node(k, tuple_n.size), respondant)'':
        * Esegue ''ttl_db_retrieve_record(tdd, k)''. Cioè recupera il record per la chiave ''k''.
        * Attendi qualche istante per non gravare sulle prestazioni della rete.
  * Calcola ''l_n0'' = livello del massimo distinto g-nodo di ''respondant''.
  * Calcola ''p_n0'' = posizione del massimo distinto g-nodo di ''respondant''.
  * ''tuple_n'' = una tupla con tutte le posizioni del nodo da 0 a ''l_n0'' inclusi.
  * Prepara ''exclude_tuple_list'' = [] una lista di istanze di tuple globali nel g-nodo di ricerca di livello ''l_n0'' + 1.
  * Per ''i'' da 0 a ''gsize[l_n0]'' - 1: Se ''i'' ≠ ''p_n0'': Metti in ''exclude_tuple_list'' il g-nodo (''l_n0'', ''i''), espresso come PeerTupleGNode di livello ''l_n0'' nel g-nodo di livello ''l_n0'' + 1.
  * Metti in ''exclude_tuple_list'' il nodo ''respondant'', espresso come PeerTupleGNode di livello 0 nel g-nodo di livello ''l_n0'' + 1.
  * While ''tdd.my_records_size()'' + ''tdd.tdh.retrieving_keys.size'' < ''tdd.max_records'':
   * # la memoria destinata da ''n'' al servizio ''p'' non è esaurita.
   * Attendi qualche istante per non gravare sulle prestazioni della rete.
   * ''respondant'' = null. Sarà una tupla nel g-nodo di livello ''l_n0'' + 1, poiché ''tuple_n'' ha ''l_n0'' + 1 elementi.
   * Esegue ''ret = contact_peer(tdd.p_id, tuple_n, r, tdd.timeout_exec_send_keys, True, out respondant, exclude_tuple_list)''.
   . Il valore restituito dovrebbe essere un !RequestSendKeysResponse, cioè una lista di Object. Altrimenti la risposta viene ignorata.
   * Se ''ret'' è una istanza di !RequestSendKeysResponse:
    * Per ogni chiave ''k'' in ''ret'':
     * Se ''tdd.is_valid_key(k)'':
      * Se '''not''' ''tdd.my_records_contains(k)'' '''e''' ''k'' ∉ ''tdd.tdh.removed_keys'' '''e''' '''not''' ''tdd.tdh.retrieving_keys.has_key(k)'':
       * # Non sa nulla di ''k''.
       * Se ''dist(tdd.evaluate_hash_node(k, tuple_n.size), tuple_n)'' < ''dist(tdd.evaluate_hash_node(k, tuple_n.size), respondant)'':
        * Esegue ''ttl_db_retrieve_record(tdd, k)''. Cioè recupera il record per la chiave ''k''.
        * Se ''tdd.my_records_size()'' + ''tdd.tdh.retrieving_keys.size'' ≥ ''tdd.max_records'': Esci dal ciclo '''for'''.
        * Attendi qualche istante per non gravare sulle prestazioni della rete.
    * Se ''tdd.my_records_size()'' + ''tdd.tdh.retrieving_keys.size'' ≥ ''tdd.max_records'': Esci dal ciclo '''while'''.
   * Metti in ''exclude_tuple_list'' il nodo ''respondant'', espresso come PeerTupleGNode di livello 0 nel g-nodo di livello ''l_n0'' + 1.
 * Se riceve !PeersNoParticipantsInNetworkError:
  # L'algoritmo termina.
 * Se riceve !PeersRefuseExecutionError:
  # L'algoritmo termina.

'''IPeersResponse ttl_db_got_request(ITemporalDatabaseDescriptor tdd, IPeersRequest r, int common_lvl) throws !PeersRefuseExecutionError'''
 * Durante l'esecuzione del suo metodo ''exec'', la classe !PeerService del servizio ''p'' che ha ricevuto la richiesta ''r'', può chiamare il metodo ''ttl_db_got_request'' per elaborare tale richiesta, posto che la richiesta rientri in uno di questi casi:
  * Una richiesta da cui si possa ottenere una unica chiave ''k''; per tale chiave la richiesta ''r'' prevede la sola lettura '''o''' la sola sovrascrittura/inserimento/cancellazione '''o''' la lettura seguita dalla sovrascrittura/cancellazione.
  * Una istanza di !RequestWaitThenSendRecord.
  * Una istanza di !RequestSendKeys.
 . Gli argomenti del metodo ''ttl_db_got_request'' sono:
  * ''tdd'': istanza di una classe che implementa ITemporalDatabaseDescriptor sopra descritta.
  * ''r'': la richiesta.
  * ''common_lvl'': il livello del minimo comune g-nodo con il richiedente, da 0 a ''levels'' compresi.
 . Al termine del metodo ''ttl_db_got_request'' viene restituita l'istanza di IPeersResponse che la classe !PeerService del servizio ''p'' dovrà restituire al chiamante. Oppure viene lanciata l'eccezione !PeersRefuseExecutionError prevista dal metodo ''exec'' di !PeerService.
 * Se ''r'' è una istanza di !RequestSendKeys:
  * # Chi riceve tale richiesta risponde subito con tutte le chiavi ''k'' dei record che detiene in memoria.
  * ''ret'' = new !RequestSendKeysResponse().
  * Per ogni chiave ''Object k'' in ''tdd.get_all_keys()'':
   * Aggiungi ''k'' a ''ret''.
  * Return ''ret''.
  * # L'algoritmo termina.
 * Se ''r'' è una istanza di !RequestWaitThenSendRecord:
  * # Chi riceve tale richiesta la tratta come una normale richiesta di sola lettura per la chiave ''k'', a parte l'attesa.
  * ''Object k'' = ''r.k''.
  * Se ''tdd.tdh.timer_non_exhaustive'' '''non''' è espirato:
   * Se ''tdd.my_records_contains(k)'':
    * Calcola ''δ'' il tempo critico di coerenza, basandosi sul numero approssimato di nodi nel minimo comune g-nodo tra il nodo corrente e quello del richiedente.
    * Attende ''δ''.
    * Se ''tdd.my_records_contains(k)'':
     * ''ret'' = new !RequestWaitThenSendRecordResponse().
     * ''ret.record'' = ''tdd.get_record_for_key(k)''.
     * Return ''ret''.
     * # L'algoritmo termina.
    * Altrimenti:
     * Return new !RequestWaitThenSendRecordNotFound(). Restituisce al richiedente l'eccezione NOT_FOUND.
     * # L'algoritmo termina.
   * Altrimenti-Se ''k'' ∈ ''tdd.tdh.removed_keys'':
    * Return new !RequestWaitThenSendRecordNotFound(). Restituisce al richiedente l'eccezione NOT_FOUND.
    * # L'algoritmo termina.
   * Altrimenti-Se ''k'' ∈ ''tdd.tdh.retrieving_keys.keys'':
    * Lancia l'eccezione ''!PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE''. Rifiuta di elaborare ''r'' (perché ''non esaustivo'').
    * # L'algoritmo termina.
   * Altrimenti:
    * # Non sa nulla di ''k''.
    * Se ''tdd.my_records_size()'' + ''tdd.tdh.retrieving_keys.size'' < ''tdd.max_records'', la memoria destinata da ''n'' al servizio ''p'' non è esaurita:
     * Mette un nuovo canale in ''tdd.tdh.retrieving_keys[k]''.
     * In una nuova tasklet:
      * Esegue ''ttl_db_retrieve_record(tdd, k)''. Cioè recupera il record per la chiave ''k''.
    * ''tdd.tdh.timer_non_exhaustive'' = un nuovo timer che scade dopo ''tdd.msec_ttl'' millisecondi.
    * Lancia l'eccezione ''!PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE''. Rifiuta di elaborare ''r'' (perché ''non esaustivo'').
    * # L'algoritmo termina.
  * Altrimenti:
   * # È ''esaustivo''.
   * Svuota la lista ''tdd.tdh.removed_keys''.
   * Se ''tdd.my_records_contains(k)'':
    * Calcola ''δ'' il tempo critico di coerenza, basandosi sul numero approssimato di nodi nel minimo comune g-nodo tra il nodo corrente e quello del richiedente.
    * Attende ''δ''.
    * Se ''tdd.my_records_contains(k)'':
     * ''ret'' = new !RequestWaitThenSendRecordResponse().
     * ''ret.record'' = ''tdd.get_record_for_key(k)''.
     * Return ''ret''.
     * # L'algoritmo termina.
    * Altrimenti:
     * Return new !RequestWaitThenSendRecordNotFound(). Restituisce al richiedente l'eccezione NOT_FOUND.
     * # L'algoritmo termina.
   * Altrimenti:
    * Return new !RequestWaitThenSendRecordNotFound(). Restituisce al richiedente l'eccezione NOT_FOUND.
    * # L'algoritmo termina.
 * ''Object k'' = ''tdd.get_key_from_request(r)''.
 * Se ''tdd.tdh.timer_non_exhaustive'' '''non''' è espirato:
  * Se ''tdd.my_records_contains(k)'':
   * ''ret'' = ''tdd.execute_request(r)''. Elabora normalmente la richiesta ''r''.
   * Se '''not''' ''tdd.my_records_contains(k)'':
    * Mette ''k'' in ''tdd.tdh.removed_keys''.
   * Return ''ret''.
   * # L'algoritmo termina.
  * Altrimenti-Se ''k'' ∈ ''tdd.tdh.removed_keys'':
   * Se ''tdd.is_read_only_request(r)'' '''o''' ''tdd.is_read_write_request(r)'', cioè ''r'' è di lettura o lettura+scrittura:
    * Return ''tdd.prepare_response_not_found(r, k)''. Restituisce al richiedente l'eccezione NOT_FOUND.
    * # L'algoritmo termina.
   * Altrimenti:
    * # ''r'' è di scrittura:
    * Rimuove ''k'' da ''tdd.tdh.removed_keys''.
    * Se ''tdd.my_records_size()'' + ''tdd.tdh.retrieving_keys.size'' < ''tdd.max_records'', la memoria destinata da ''n'' al servizio ''p'' non è esaurita:
     * Return ''tdd.execute_request(r)''. Elabora normalmente la richiesta ''r'', aggiungendo un record in memoria.
     * # L'algoritmo termina.
    * Altrimenti:
     * ''tdd.tdh.timer_non_exhaustive'' = un nuovo timer che scade dopo ''tdd.msec_ttl'' millisecondi.
     * Lancia l'eccezione ''!PeersRefuseExecutionError.WRITE_OUT_OF_MEMORY''. Rifiuta di elaborare ''r'' (perché ''out of memory'').
     * # L'algoritmo termina.
  * Altrimenti-Se ''k'' ∈ ''tdd.tdh.retrieving_keys.keys'':
   * Se ''tdd.is_read_only_request(r)'', cioè ''r'' è di sola lettura:
    * Lancia l'eccezione ''!PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE''. Rifiuta di elaborare ''r'' (perché ''non esaustivo'').
    * # L'algoritmo termina.
   * Altrimenti:
    * # ''r'' è di scrittura o lettura+scrittura:
    * Attende di ricevere una comunicazione dal canale ''tdd.tdh.retrieving_keys[k]''.
    * Se ''tdd.is_read_write_request(r)'', cioè ''r'' prevede una lettura prima della scrittura:
     * Se '''not''' ''tdd.my_records_contains(k)'':
      * # Il nodo non ha il record ancora.
      * Mette ''k'' in ''tdd.tdh.removed_keys''.
      * Return ''tdd.prepare_response_not_found(r, k)''. Restituisce al richiedente l'eccezione NOT_FOUND.
      * # L'algoritmo termina.
     * Altrimenti:
      * Return ''tdd.execute_request(r)''. Elabora normalmente la richiesta ''r''.
      * # L'algoritmo termina.
    * Altrimenti:
     * Return ''tdd.execute_request(r)''. Elabora normalmente la richiesta ''r''.
     * # L'algoritmo termina.
  * Altrimenti:
   * # Non sa nulla di ''k''.
   * Se ''tdd.my_records_size()'' + ''tdd.tdh.retrieving_keys.size'' < ''tdd.max_records'', la memoria destinata da ''n'' al servizio ''p'' non è esaurita:
    * Mette un nuovo canale in ''tdd.tdh.retrieving_keys[k]''.
    * In una nuova tasklet:
     * Esegue ''ttl_db_retrieve_record(tdd, k)''. Cioè recupera il record per la chiave ''k''.
   * ''tdd.tdh.timer_non_exhaustive'' = un nuovo timer che scade dopo ''tdd.msec_ttl'' millisecondi.
   * Lancia l'eccezione ''!PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE''. Rifiuta di elaborare ''r'' (perché ''non esaustivo'').
   * # L'algoritmo termina.
 * Altrimenti:
  * # È ''esaustivo''.
  * Svuota la lista ''tdd.tdh.removed_keys''.
  * Se ''tdd.my_records_contains(k)'':
   * Return ''tdd.execute_request(r)''. Elabora normalmente la richiesta ''r''.
   * # L'algoritmo termina.
  * Altrimenti:
   * Se ''tdd.is_read_only_request(r)'' '''o''' ''tdd.is_read_write_request(r)'', cioè ''r'' è di lettura o lettura+scrittura:
    * Return ''tdd.prepare_response_not_found(r, k)''. Restituisce al richiedente l'eccezione NOT_FOUND.
    * # L'algoritmo termina.
   * Altrimenti:
    * # ''r'' è di scrittura:
    * Se ''tdd.my_records_size()'' + ''tdd.tdh.retrieving_keys.size'' < ''tdd.max_records'', la memoria destinata da ''n'' al servizio ''p'' non è esaurita:
     * Return ''tdd.execute_request(r)''. Elabora normalmente la richiesta ''r'', aggiungendo un record in memoria.
     * # L'algoritmo termina.
    * Altrimenti:
     * ''tdd.tdh.timer_non_exhaustive'' = un nuovo timer che scade dopo ''tdd.msec_ttl'' millisecondi.
     * Lancia l'eccezione ''!PeersRefuseExecutionError.WRITE_OUT_OF_MEMORY''. Rifiuta di elaborare ''r'' (perché ''out of memory'').
     * # L'algoritmo termina.

'''internal void ttl_db_retrieve_record(ITemporalDatabaseDescriptor tdd, Object k)'''
 * Questo metodo è interno al modulo. Viene chiamato dai metodi sopra descritti (begin e got_request) per recuperare il record per la chiave ''k''.
 . Gli argomenti del metodo ''ttl_db_retrieve_record'' sono:
  * ''tdd'': istanza di una classe che implementa ITemporalDatabaseDescriptor sopra descritta.
  * ''k'': la chiave.
 . Al termine del metodo ''ttl_db_retrieve_record'' il record è stato recuperato e messo in memoria, oppure la chiave è stata messa nell'elenco di chiavi da considerare definitivamente assenti anche in caso di nodo ''non esaustivo''.
 * ''IPeersRequest r'' = new ''!RequestWaitThenSendRecord(k)''.
 * Try:
  * ''!PeerTupleNode respondant'' = null.
  * Esegue ''IPeersResponse ret = contact_peer(tdd.p_id, tdd.evaluate_hash_node(k, levels), r, tdd.timeout_exec_wait_then_send_record, True, out respondant)''.
  * Se ''ret'' è una istanza di !RequestWaitThenSendRecordNotFound:
   * Se ''k'' ∉ ''tdd.tdh.removed_keys'':
    * Aggiunge ''k'' a ''tdd.tdh.removed_keys''.
   * ''INtkdChannel temp_ch'' = ''tdd.tdh.retrieving_keys[k]''.
   * ''tdd.tdh.retrieving_keys.unset(k)''. Rimuove ''k'' da ''tdd.tdh.retrieving_keys''.
   * Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su ''temp_ch''.
  * Altrimenti-Se ''ret'' è una istanza di !RequestWaitThenSendRecordResponse:
   * Esegue ''tdd.set_record_for_key(k, ret.record)''. Scrive il record nella sua memoria.
   * Se ''k'' ∈ ''tdd.tdh.removed_keys'':
    * Rimuove ''k'' da ''tdd.tdh.removed_keys''.
   * ''INtkdChannel temp_ch'' = ''tdd.tdh.retrieving_keys[k]''.
   * ''tdd.tdh.retrieving_keys.unset(k)''. Rimuove ''k'' da ''tdd.tdh.retrieving_keys''.
   * Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su ''temp_ch''.
 * Se riceve !PeersNoParticipantsInNetworkError:
  * Se ''k'' ∉ ''tdd.tdh.removed_keys'':
   * Aggiunge ''k'' a ''tdd.tdh.removed_keys''.
  * ''INtkdChannel temp_ch'' = ''tdd.tdh.retrieving_keys[k]''.
  * ''tdd.tdh.retrieving_keys.unset(k)''. Rimuove ''k'' da ''tdd.tdh.retrieving_keys''.
  * Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su ''temp_ch''.
 * Se riceve !PeersRefuseExecutionError:
  * Se ''k'' ∉ ''tdd.tdh.removed_keys'':
   * Aggiunge ''k'' a ''tdd.tdh.removed_keys''.
  * ''INtkdChannel temp_ch'' = ''tdd.tdh.retrieving_keys[k]''.
  * ''tdd.tdh.retrieving_keys.unset(k)''. Rimuove ''k'' da ''tdd.tdh.retrieving_keys''.
  * Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su ''temp_ch''.

==== servizi con un numero preciso ed esiguo di possibili chiavi ====
I metodi ''fixed_keys_db_*'' servono a gestire un database distribuito in cui le possibili chiavi sono in un set definito e in numero esiguo. Il modulo, oltre a fornire tali metodi, fornisce la classe !FixedKeysDatabaseHandler, l'interfaccia IFixedKeysDatabaseDescriptor.

La classe !RequestNop è una classe serializzabile, che deriva da Object, e non ha alcun dato al suo interno. Implementa l'interfaccia (vuota) IPeersRequest. È una finta richiesta, serve solo a contattare il detentore corrente del record di una chiave e vederne l'indirizzo. Tale classe viene esposta dal modulo, in quanto la classe del servizio deve poter riconoscere una sua istanza, ma i suoi membri non sono esposti, incluso il costruttore.

La classe !RequestNopResponse è una classe serializzabile, che deriva da Object, e non ha alcun dato al suo interno. Implementa l'interfaccia (vuota) IPeersResponse. È la risposta alla finta richiesta di cui sopra. Tale classe non viene esposta dal modulo.

La classe !FixedKeysDatabaseHandler è esposta dal modulo, ma il suo contenuto è del tutto oscuro all'esterno del modulo. I suoi membri, accessibili solo dal modulo, sono:
 * ''internal !FixedKeysDatabaseHandler'': lo stesso costruttore della classe è accessibile solo dal modulo.
 * ''internal List<Object> not_started_keys'': una lista di chiavi di cui non abbiamo avviato il recupero.
 * ''internal !HashMap<Object,INtkdChannel> retrieving_keys'': una mappa che associa ad una chiave un canale di comunicazione tra tasklet se per tale chiave è in corso una operazione di recupero.

L'interfaccia IFixedKeysDatabaseDescriptor espone questi metodi:
 * ''!FixedKeysDatabaseHandler fkdh'': una proprietà destinata ad essere popolata ed acceduta dal modulo con una istanza di !FixedKeysDatabaseHandler.
 * ''int p_id'': una proprietà che dice l'identificativo di questo servizio.
 * ''int timeout_exec_nop'': una proprietà che dice il tempo massimo di esecuzione per la richiesta !RequestNop per questo servizio.
 * ''int timeout_exec_wait_then_send_record'': una proprietà che dice il tempo massimo di esecuzione per la richiesta !RequestWaitThenSendRecord per questo servizio.
 * ''List<int> evaluate_hash_node(Object k, int lvl)'': una funzione che calcola la tupla ''h~-,,p,,-~(k)'' di questo servizio per la chiave ''k'' tenendo in considerazione che servono solo le posizioni interne al livello ''lvl''.
 * ''bool key_equal_data(Object k1, Object k2)'' e ''uint key_hash_data(Object k)'': metodi le cui firme sono adatte per i delegati ''Gee.!EqualDataFunc<Object>'' e ''Gee.!HashDataFunc<Object>'', per costruire una !HashMap o una lista "con funzionalità di ricerca" di chiavi del servizio.
 * ''Object get_key_from_request(IPeersRequest r)'': una funzione che partendo dalla richiesta ''r'', la quale non deve essere una istanza di !RequestWaitThenSendRecord, restituisce la chiave ''k'' a cui si riferisce.
 * ''bool is_read_only_request(IPeersRequest r)'': una funzione che dice se la richiesta è di sola lettura.
 * ''IPeersResponse execute_request(IPeersRequest r)'': una funzione che elabora la richiesta ''r'', la quale non deve essere una istanza di !RequestWaitThenSendRecord, e produce una istanza di IPeersResponse che possa essere usata come valore di ritorno.
 * ''Object get_record_for_key(Object k)'': una funzione che restituisce il record per la chiave ''k'', avendo come requisito (cioè il chiamante deve averlo già verificato) che tale chiave è presente nella memoria.
 * ''void set_record_for_key(Object k, Object rec)'': una funzione che mette in memoria il record ''rec'' per la chiave ''k''. Questa funzione deve garantire di essere atomica, cioè di non schedulare altre tasklet.
 * ''void set_record_to_default_for_key(Object k)'': una funzione che mette in memoria un adeguato valore di default per la chiave ''k''. Questa funzione deve garantire di essere atomica, cioè di non schedulare altre tasklet.

'''void fixed_keys_db_begin(IFixedKeysDatabaseDescriptor fkdd, List<Object> K)'''
 * Viene avviata in una tasklet dalla classe del servizio, che deriva !PeerService.
 . Se si tratta di un servizio opzionale, viene chiamata solo dopo che sono state reperite con successo le mappe dei partecipanti ai servizi opzionali.
 . Gli argomenti del metodo ''fixed_keys_db_begin'' sono:
  * ''K'': la lista di tutte le possibili chiavi.
  * ''tuple_n'': una tupla con tutte le posizioni del nodo da 0 a ''levels'' - 1.
 * ''fkdd.fkdh'' = new !FixedKeysDatabaseHandler().
 * ''fkdd.fkdh.not_started_keys'' = new !ArrayList<Object>(''fkdd.key_equal_data'').
 * ''fkdd.fkdh.retrieving_keys'' = new !HashMap<Object,INtkdChannel>(''fkdd.key_equal_data'', ''fkdd.key_hash_data'').
 * Per ogni chiave ''k'' nell'insieme completo ''K'':
  * Aggiunge ''k'' a ''fkdd.fkdh.not_started_keys''.
 * Per ogni chiave ''k'' nell'insieme completo ''K'':
  * # Sia ''respondant'' il detentore corrente di ''k''. Lo trova con una chiamata a contact_peer in cui non chiede nulla.
  * ''!PeerTupleNode respondant'' = null. Sarà una tupla nel g-nodo di livello ''levels'', poiché ''tuple_n'' ha ''levels'' elementi.
  * ''IPeersRequest r'' = new !RequestNop().
  * Esegue ''IPeersResponse ret = contact_peer(fkdd.p_id, tuple_n, r, fkdd.timeout_exec_nop, True, out respondant)''.
  * Se ''dist(fkdd.evaluate_hash_node(k, tuple_n.size), tuple_n)'' < ''dist(fkdd.evaluate_hash_node(k, tuple_n.size), respondant)'':
   * Mette un nuovo canale in ''fkdd.fkdh.retrieving_keys[k]''.
   * In una nuova tasklet:
    * Esegue ''fixed_keys_db_retrieve_record(fkdd, k)''. Cioè recupera il record per la chiave ''k''.
   * Rimuove ''k'' da ''fkdd.fkdh.not_started_keys''.
   * Attendi qualche istante per non gravare sulle prestazioni della rete.
  * Altrimenti:
   * Rimuove ''k'' da ''fkdd.fkdh.not_started_keys''.

'''IPeersResponse fixed_keys_db_got_request(IFixedKeysDatabaseDescriptor fkdd, IPeersRequest r, int common_lvl) throws !PeersRefuseExecutionError'''
 * Durante l'esecuzione del suo metodo ''exec'', la classe !PeerService del servizio ''p'' che ha ricevuto la richiesta ''r'', può chiamare il metodo ''fixed_keys_db_got_request'' per elaborare tale richiesta, posto che la richiesta rientri in uno di questi casi:
  * Una richiesta da cui si possa ottenere una unica chiave ''k''; per tale chiave la richiesta ''r'' prevede la sola lettura '''o''' la sola scrittura '''o''' la lettura seguita dalla scrittura.
  * Una istanza di !RequestWaitThenSendRecord.
  * Una istanza di !RequestNop.
 . Gli argomenti del metodo ''fixed_keys_db_got_request'' sono:
  * ''fkdd'': istanza di una classe che implementa IFixedKeysDatabaseDescriptor sopra descritta.
  * ''r'': la richiesta.
  * ''common_lvl'': il livello del minimo comune g-nodo con il richiedente, da 0 a ''levels'' compresi.
 . Al termine del metodo ''fixed_keys_db_got_request'' viene restituita l'istanza di IPeersResponse che la classe !PeerService del servizio ''p'' dovrà restituire al chiamante. Oppure viene lanciata l'eccezione !PeersRefuseExecutionError prevista dal metodo ''exec'' di !PeerService.
 * Se ''r'' è una istanza di !RequestWaitThenSendRecord:
  * # Chi riceve tale richiesta la tratta come una normale richiesta di sola lettura per la chiave ''k'', a parte l'attesa.
  * ''Object k'' = ''r.k''.
  * Se ''k'' ∈ ''fkdd.fkdh.not_started_keys'':
   * Lancia l'eccezione ''!PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE''. Rifiuta di elaborare ''r'' (perché ''non esaustivo'').
   * # L'algoritmo termina.
  * Altrimenti-Se ''k'' ∈ ''fkdd.fkdh.retrieving_keys.keys'':
   * Lancia l'eccezione ''!PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE''. Rifiuta di elaborare ''r'' (perché ''non esaustivo'').
   * # L'algoritmo termina.
  * Altrimenti:
   * Calcola ''δ'' il tempo critico di coerenza, basandosi sul numero approssimato di nodi nel minimo comune g-nodo tra il nodo corrente e quello del richiedente.
   * Attende ''δ''.
   * ''ret'' = new !RequestWaitThenSendRecordResponse().
   * ''ret.record'' = ''fkdd.get_record_for_key(k)''.
   * Return ''ret''.
   * # L'algoritmo termina.
 * Se ''r'' è una istanza di !RequestNop:
  * # Chi riceve tale richiesta risponde subito, anche se fosse ''non esaustivo''.
  * Return new !RequestNopResponse().
  * # L'algoritmo termina.
 * ''Object k'' = ''fkdd.get_key_from_request(r)''.
 * Se ''k'' ∈ ''fkdd.fkdh.not_started_keys'':
  * Lancia l'eccezione ''!PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE''. Rifiuta di elaborare ''r'' (perché ''non esaustivo'').
  * # L'algoritmo termina.
 * Altrimenti-Se ''k'' ∈ ''fkdd.fkdh.retrieving_keys.keys'':
  * Se ''fkdd.is_read_only_request(r)'', cioè ''r'' è di sola lettura:
   * Lancia l'eccezione ''!PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE''. Rifiuta di elaborare ''r'' (perché ''non esaustivo'').
   * # L'algoritmo termina.
  * Altrimenti:
   * # ''r'' è di scrittura:
   * Attende di ricevere una comunicazione dal canale ''fkdd.fkdh.retrieving_keys[k]''.
   * Return ''fkdd.execute_request(r)''. Elabora normalmente la richiesta ''r''.
   * # L'algoritmo termina.
 * Altrimenti:
  * Return ''fkdd.execute_request(r)''. Elabora normalmente la richiesta ''r''.
  * # L'algoritmo termina.

'''internal void fixed_keys_db_retrieve_record(IFixedKeysDatabaseDescriptor fkdd, Object k)'''
 * Questo metodo è interno al modulo. Viene chiamato dai metodi sopra descritti (begin e got_request) per recuperare il record per la chiave ''k''.
 . Gli argomenti del metodo ''fixed_keys_db_retrieve_record'' sono:
  * ''fkdd'': istanza di una classe che implementa IFixedKeysDatabaseDescriptor sopra descritta.
  * ''k'': la chiave.
 . Al termine del metodo ''fixed_keys_db_retrieve_record'' il record è stato recuperato e messo in memoria, oppure la memoria è stata valorizzata con un apposito default.
 * ''IPeersRequest r'' = new ''!RequestWaitThenSendRecord(k)''.
 * Try:
  * Esegue ''IPeersResponse ret = contact_peer(fkdd.p_id, fkdd.evaluate_hash_node(k, levels), r, fkdd.timeout_exec_wait_then_send_record, True, out respondant)''.
  * Se ''ret'' è una istanza di !RequestWaitThenSendRecordResponse:
   * Esegue ''fkdd.set_record_for_key(k, ret.record)''. Scrive il record nella sua memoria.
  * Altrimenti:
   * Esegue ''fkdd.set_record_to_default_for_key(k)''. Valorizza la sua memoria con un appropriato default.
 * Se riceve !PeersNoParticipantsInNetworkError:
  * Esegue ''fkdd.set_record_to_default_for_key(k)''. Valorizza la sua memoria con un appropriato default.
 * Se riceve !PeersRefuseExecutionError:
  * Esegue ''fkdd.set_record_to_default_for_key(k)''. Valorizza la sua memoria con un appropriato default.
 * ''INtkdChannel temp_ch'' = ''fkdd.fkdh.retrieving_keys[k]''.
 * ''fkdd.fkdh.retrieving_keys.unset(k)''. Rimuove ''k'' da ''fkdd.fkdh.retrieving_keys''.
 * Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su ''temp_ch''.

Modulo PeerServices - Appunti - Algoritmi 2 / 2

Algoritmo di rilevamento di non partecipazione

check_non_participation

bool check_non_participation(p_id, lvl, _pos)

  • In questo algoritmo non ci interessa sapere se un g-nodo partecipa, ma solo se è possibile dire con certezza che esso non partecipa. In caso di incertezza l'algoritmo restituisce False.
  • Produci = la tupla x̄0·x̄1·...·x̄lvl-1 dove x̄i = 0 per ogni i. La tupla identifica un indirizzo a caso all'interno del g-nodo g = (lvl, _pos). Se lvl = 0 allora x̄ è null.

  • Produci n = make_tuple_node(new HCoord(0, pos[0]), lvl+1) , cioè la tupla n0·n1·...·nlvl. La tupla che identifica il nodo corrente nel g-nodo di livello lvl+1 in cui il messaggio si muoverà.

  • m’ = new PeerMessageForwarder.

  • m’.n = n.

  • m’.x̄ = x̄.

  • m’.lvl = lvl.

  • m’.pos = _pos.

  • m’.p_id = p_id.

  • m’.msg_id = un identificativo generato a caso per questo messaggio.

  • Calcola timeout_instradamento = f ( map_paths.i_peers_get_nodes_in_my_group(lvl + 1) ).

  • Prepara waiting_answer = new WaitingAnswer(null, (lvl,_pos) come PeerTupleGNode con top = lvl+1). Il fatto che l'istanza di IPeersRequest è a null fa in modo che i metodi remoti che ricevono le notifiche si comportano in modo adeguato. Sostanzialmente dovrebbe cambiare solo il fatto che quando si riceve la segnalazione di get_request si risponde sempre con l'eccezione PeersUnknownMessageError, anche se si è potuto recuperare l'istanza di WaitingAnswer. Sull'istanza di WaitingAnswer viene poi valorizzato il membro response con qualcosa diverso da null solo per indicare che il g-nodo partecipa.

  • waiting_answer_map[m’.msg_id] = waiting_answer.

  • IPeersManagerStub gwstub

  • IPeersManagerStub? failed = null

  • While True:
    • Try:
      • Calcola gwstub = map_paths.i_peers_gateway(lvl, _pos, null, failed)
    • Se riceve l'eccezione PeersNonexistentDestinationError:

      • Restituisci True. Rimuovi waiting_answer_map[m’.msg_id]. Termina algoritmo.
    • Try:
      • Esegue gwstub.forward_peer_message(m’).
    • Se riceve StubError o DeserializeError:

      • failed = gwstub.
      • Continua con la prossima iterazione del ciclo.
    • Esci dal ciclo.
  • Try:
    • Sta in attesa su waiting_answer.ch per max timeout_instradamento.
    • Se waiting_answer.exclude_gnode ≠ null:
      • Restituisce False. Rimuovi waiting_answer_map[m’.msg_id]. Termina algoritmo.
    • Altrimenti-Se waiting_answer.non_participant_gnode ≠ null:
      • # significa che abbiamo ricevuto notizia di un gnodo non partecipante.
      • waiting_answer.non_participant_gnode è un PeerTupleGNode che rappresenta un g-nodo h dentro il mio g-nodo di livello top.

      • Se è visibile nella mia mappa, cioè se (lvl,top) non partecipa:
        • Restituisci True. Rimuovi waiting_answer_map[m’.msg_id]. Termina algoritmo.
      • Altrimenti:
        • Restituisce False. Rimuovi waiting_answer_map[m’.msg_id]. Termina algoritmo.
    • Altrimenti-Se waiting_answer.response ≠ null:
      • # significa che abbiamo ricevuto il contatto e che lvl,_pos partecipa.
      • Restituisce False. Rimuovi waiting_answer_map[m’.msg_id]. Termina algoritmo.
    • Altrimenti:
      • # significa che abbiamo ricevuto un nuovo valore in waiting_answer.min_target.
      • Restituisce False. Rimuovi waiting_answer_map[m’.msg_id]. Termina algoritmo.
  • Se riceve l'eccezione TimeoutError:

    • # dobbiamo trattare waiting_answer.min_target come da escludere.
    • Restituisce False. Rimuovi waiting_answer_map[m’.msg_id]. Termina algoritmo.

Algoritmo di divulgazione della partecipazione

publish_my_participation

void publish_my_participation(p_id)

  • gn = make_tuple_gnode(new HCoord(0, pos[0]), levels). La tupla n0·n1·...·nlevels-1, che identifica il nodo corrente nella rete.

  • tempo_attesa = 300 secondi.

  • iterazioni = 5.

  • While True (per sempre):
    • Se iterazioni > 0:

      • Decrementa iterazioni di 1.
    • Altrimenti:
      • tempo_attesa = 1 giorno + random(1..24*60*60) secondi.
    • Prepara un IPeersMissingArcHandler missing_handler che in caso di invocazione esegua:

      • Calcola tcp_stub = neighbors_factory.i_peers_get_tcp(missing_arc).
      • Try:
        • tcp_stub.set_participant(p_id, gn).
      • Se riceve StubError o DeserializeError:

        • Ignora.
    • Calcola br_stub = neighbors_factory.i_peers_get_broadcast(missing_handler).
    • Try:
      • br_stub.set_participant(p_id, gn).
    • Se riceve StubError o DeserializeError:

      • Ignora.
    • Aspetta tempo_attesa.

set_participant

void set_participant(int p_id, PeerTupleGNode gn)

  • E' già stata istanziata lista_recenti un ArrayList di HCoord.

  • Se services.has_key(p_id) AND NOT services[p_id].p_is_optional:
    • Ignora il messaggio. Algoritmo termina.
  • int case, HCoord ret.

  • Calcola convert_tuple_gnode(gn, out case, out ret).
  • Se case = 1:
    • Cioè gn rappresenta un mio g-nodo.
    • Ignora il messaggio. Algoritmo termina.
  • Altrimenti:
    • Cioè gn rappresenta un g-nodo a cui io non appartengo, ed ho già calcolato in ret il g-nodo visibile nella mia topologia in cui gn si trova.
    • Se ret ∈ lista_recenti:
      • Ignora il messaggio. Algoritmo termina.
    • Altrimenti:
      • lista_recenti.add(ret).
      • Se NOT participant_maps.has_key(p_id):
        • participant_maps[p_id] = new ParticipantMap().

      • participant_maps[p_id].participant_list.add(ret).
      • ret_gn = make_tuple_gnode(ret, levels)

      • Prepara un IPeersMissingArcHandler missing_handler che in caso di invocazione esegua:

        • Calcola tcp_stub = neighbors_factory.i_peers_get_tcp(missing_arc).
        • Try:
          • tcp_stub.set_participant(p_id, ret_gn).
        • Se riceve StubError o DeserializeError:

          • Ignora.
      • Calcola br_stub = neighbors_factory.i_peers_get_broadcast(missing_handler).
      • Try:
        • br_stub.set_participant(p_id, ret_gn).
      • Se riceve StubError o DeserializeError:

        • Ignora.
      • Svolgi quanto segue in una nuova tasklet portando dietro ret:
        • Aspetta 60 secondi.
        • lista_recenti.remove(ret).

Algoritmi per il mantenimento di un database distribuito

per assicurare delle repliche

bool begin_replica(q, p_id, x̄, r, timeout_exec, out IPeersResponse? resp, out IPeersContinuation cont)

  • Gli argomenti sono:
    • int q: il numero delle repliche richieste,

    • int p_id,

    • PeerTupleNode : la tupla dell'hash della chiave del record, cioè hp ( k ),

    • IPeersRequest r: la richiesta di replicare la coppia k,val ,

    • int timeout_exec,

    • resp viene valorizzato con la risposta o null;

    • cont è un oggetto di cui all'esterno si sa solo che implementa l'interfaccia vuota IPeersContinuation.

  • lista_repliche = new List di PeerTupleNode.

  • exclude_tuple_list = new PeerTupleGNodeContainer(x̄.tuple.size).

  • cont = {q, p_id, x̄, r, timeout_exec, lista_repliche, exclude_tuple_list}.
  • Return next_replica(cont, out resp).

bool next_replica(IPeersContinuation cont, out IPeersResponse? resp)

  • resp = null.
  • Se cont.lista_repliche.size ≥ cont.q:
    • Return False.
  • PeerTupleNode respondant;

  • ret = contact_peer(cont.p_id, cont.x̄, cont.r, cont.timeout_exec, True, out respondant, cont.exclude_tuple_list).
  • Se si riceve l'eccezione PeersNoParticipantsInNetworkError o PeersRefuseExecutionError:

    • Return False.
  • resp = ret.
  • aggiungi respondant a cont.lista_repliche.
  • aggiungi respondant a cont.exclude_tuple_list.
  • Return cont.lista_repliche.size < cont.q.

per reperire i record di pertinenza nella fase di ingresso in una rete

Il modulo fornisce alcuni algoritmi alle classi che implementano servizi che prevedono il mantenimento di database distribuiti. Con questi algoritmi tali classi sono facilitate nel compito di reperire i record di pertinenza nella fase di ingresso in una rete. Questi algoritmi pongono alcune regole che la classe del servizio deve rispettare:

  • Deve essere possibile rappresentare la chiave di un record con una istanza di una classe derivata da Object che sia serializzabile.

  • Deve essere possibile rappresentare il contenuto di un record con una istanza di una classe derivata da Object che sia serializzabile. Tale classe non deve necessariamente contenere anche la chiave.

  • Non viene imposto nessun obbligo riguardo il formato delle classi che rappresentano le richieste del servizio. La classe del servizio deve essere in grado di costruire una istanza della chiave interessata partendo dalla istanza che rappresenta la richiesta. Deve essere inoltre in grado di stabilire se la richiesta è di sola lettura, di sola scrittura, o di lettura e successiva scrittura.
  • Non viene imposto nessun obbligo riguardo il formato delle classi che rappresentano le risposte del servizio. La classe del servizio deve essere in grado di costruire una risposta che indica l'eccezione NOT_FOUND per una certa chiave.

Il modulo fornisce la classe RequestWaitThenSendRecord. Si tratta di una classe serializzabile, che deriva da Object, e contiene una istanza di Object serializzabile k. Implementa l'interfaccia (vuota) IPeersRequest. È la richiesta di aspettare un tempo δ (valutato direttamente dal nodo che riceve la richiesta) e poi inviare il record relativo alla chiave k. Tale classe viene esposta dal modulo, in quanto la classe del servizio deve poter riconoscere una sua istanza, ma i suoi membri non sono esposti, incluso il costruttore.

Il modulo fornisce la classe RequestWaitThenSendRecordResponse. Si tratta di una classe serializzabile, che deriva da Object, e contiene una istanza di Object serializzabile record. Implementa l'interfaccia (vuota) IPeersResponse. È la risposta alla richiesta sopra descritta. Tale classe non viene esposta dal modulo.

Il modulo fornisce la classe RequestWaitThenSendRecordNotFound. Si tratta di una classe serializzabile, che deriva da Object, e non ha alcun dato al suo interno. Implementa l'interfaccia (vuota) IPeersResponse. È la risposta come eccezione NOT_FOUND alla richiesta sopra descritta. Tale classe non viene esposta dal modulo.

servizi con scadenza temporale dei record

I metodi ttl_db_* servono a gestire un database distribuito in cui i record hanno una scadenza, o TTL. Il modulo, oltre a fornire tali metodi, fornisce la classe RequestSendKeys, la classe RequestSendKeysResponse, la classe TemporalDatabaseHandler, l'interfaccia ITemporalDatabaseDescriptor.

La classe RequestSendKeys è una classe serializzabile, che deriva da Object, e non ha alcun dato al suo interno. Implementa l'interfaccia (vuota) IPeersRequest. È la richiesta di inviare tutte le chiavi memorizzate. Tale classe viene esposta dal modulo, in quanto la classe del servizio deve poter riconoscere una sua istanza, ma i suoi membri non sono esposti, incluso il costruttore.

La classe RequestSendKeysResponse è una classe serializzabile, che deriva da Object, e contiene una lista di istanze di Object serializzabili. Implementa l'interfaccia (vuota) IPeersResponse. È la risposta alla richiesta di inviare tutte le chiavi memorizzate. Tale classe non viene esposta dal modulo.

La classe TemporalDatabaseHandler è esposta dal modulo, ma il suo contenuto è del tutto oscuro all'esterno del modulo. I suoi membri, accessibili solo dal modulo, sono:

  • internal TemporalDatabaseHandler: lo stesso costruttore della classe è accessibile solo dal modulo.

  • internal Timer timer_non_exhaustive: un timer. Se è null o è scaduto significa che il nodo si considera esaustivo per i record di sua pertinenza, altrimenti no.

  • internal List<Object> removed_keys: una lista di chiavi che sono state cancellate su richiesta, per le quali il nodo può rispondere NOT_FOUND anche se non è esaustivo.

  • internal HashMap<Object,INtkdChannel> retrieving_keys: una mappa che associa ad una chiave un canale di comunicazione tra tasklet se per tale chiave è in corso una operazione di recupero.

L'interfaccia ITemporalDatabaseDescriptor espone questi metodi:

  • TemporalDatabaseHandler tdh: una proprietà destinata ad essere popolata ed acceduta dal modulo con una istanza di TemporalDatabaseHandler.

  • int p_id: una proprietà che dice l'identificativo di questo servizio.

  • int timeout_exec_send_keys: una proprietà che dice il tempo massimo di esecuzione per la richiesta RequestSendKeys per questo servizio.

  • int timeout_exec_wait_then_send_record: una proprietà che dice il tempo massimo di esecuzione per la richiesta RequestWaitThenSendRecord per questo servizio.

  • int msec_ttl: una proprietà che dice il numero di millisecondi prima che un record di questo servizio scada.

  • int max_records: una proprietà che dice il numero massimo di record che il nodo può memorizzare per questo servizio.

  • bool is_valid_key(Object k): una funzione che dice se k è una chiave valida per il servizio.

  • List<int> evaluate_hash_node(Object k, int lvl): una funzione che calcola la tupla hp(k) di questo servizio per la chiave k tenendo in considerazione che servono solo le posizioni interne al livello lvl.

  • bool key_equal_data(Object k1, Object k2) e uint key_hash_data(Object k): metodi le cui firme sono adatte per i delegati Gee.EqualDataFunc<Object> e Gee.HashDataFunc<Object>, per costruire una HashMap o una lista "con funzionalità di ricerca" di chiavi del servizio.

  • int my_records_size(): una funzione per sapere il numero di record per questo servizio attualmente memorizzati dal nodo.

  • bool my_records_contains(Object k): una funzione per sapere se il record per la chiave k per questo servizio è attualmente memorizzato dal nodo.

  • List<Object> get_all_keys(): una funzione che restituisce la lista di chiavi detenute in memoria.

  • Object get_key_from_request(IPeersRequest r): una funzione che partendo dalla richiesta r, la quale non deve essere una istanza di RequestSendKeys, né una istanza di RequestWaitThenSendRecord, restituisce la chiave k a cui si riferisce.

  • bool is_read_only_request(IPeersRequest r): una funzione che dice se la richiesta è di sola lettura.

  • bool is_write_only_request(IPeersRequest r): una funzione che dice se la richiesta è di sola scrittura.

  • bool is_read_write_request(IPeersRequest r): una funzione che dice se la richiesta è di lettura e successiva scrittura. Per una richiesta r che non sia una istanza di RequestSendKeys, né una istanza di RequestWaitThenSendRecord, uno e uno solo di questi tre metodi restituisce True.

  • IPeersResponse execute_request(IPeersRequest r): una funzione che elabora la richiesta r, la quale non deve essere una istanza di RequestSendKeys, né una istanza di RequestWaitThenSendRecord, e produce una istanza di IPeersResponse che possa essere usata come valore di ritorno.

  • IPeersResponse prepare_response_not_found(IPeersRequest r, Object k): una funzione per produrre una istanza di IPeersResponse che possa essere usata come eccezione NOT_FOUND per la richiesta r e la chiave k.

  • Object get_record_for_key(Object k): una funzione che restituisce il record per la chiave k, avendo come requisito (cioè il chiamante deve averlo già verificato) che tale chiave è presente nella memoria.

  • void set_record_for_key(Object k, Object rec): una funzione che mette in memoria il record rec per la chiave k, avendo come requisito (cioè il chiamante deve averlo già verificato) che la memoria non sia ancora esaurita. Questa funzione deve garantire di essere atomica, cioè di non schedulare altre tasklet.

void ttl_db_begin(ITemporalDatabaseDescriptor tdd, List<int> tuple_n)

  • Viene avviata in una tasklet dalla classe del servizio, che deriva PeerService.

  • Se si tratta di un servizio opzionale, viene chiamata solo dopo che sono state reperite con successo le mappe dei partecipanti ai servizi opzionali.
  • Gli argomenti del metodo ttl_db_begin sono:

    • tdd: istanza di una classe che implementa ITemporalDatabaseDescriptor sopra descritta.

    • tuple_n: una tupla con tutte le posizioni del nodo da 0 a levels - 1.

  • tdd.tdh = new TemporalDatabaseHandler().

  • tdd.tdh.timer_non_exhaustive = un nuovo timer che scade dopo tdd.msec_ttl millisecondi.

  • tdd.tdh.removed_keys = new ArrayList<Object>(tdd.key_equal_data).

  • tdd.tdh.retrieving_keys = new HashMap<Object,INtkdChannel>(tdd.key_equal_data, tdd.key_hash_data).

  • IPeersRequest r = new RequestSendKeys().

  • Try:
    • PeerTupleNode respondant = null. Sarà una tupla nel g-nodo di livello levels, poiché tuple_n ha levels elementi.

    • Esegue IPeersResponse ret = contact_peer(tdd.p_id, tuple_n, r, tdd.timeout_exec_send_keys, True, out respondant).

    • Il valore restituito dovrebbe essere un RequestSendKeysResponse, cioè una lista di Object. Altrimenti la risposta viene ignorata.

    • Se ret è una istanza di RequestSendKeysResponse:

      • Per ogni chiave k in ret:

        • Se tdd.my_records_size() + tdd.tdh.retrieving_keys.size < tdd.max_records:

          • Se tdd.is_valid_key(k):

            • Se not tdd.my_records_contains(k) e ktdd.tdh.removed_keys e not tdd.tdh.retrieving_keys.has_key(k):

              • # Non sa nulla di k.

              • Se dist(tdd.evaluate_hash_node(k, tuple_n.size), tuple_n) < dist(tdd.evaluate_hash_node(k, tuple_n.size), respondant):

                • Esegue ttl_db_retrieve_record(tdd, k). Cioè recupera il record per la chiave k.

                • Attendi qualche istante per non gravare sulle prestazioni della rete.
    • Calcola l_n0 = livello del massimo distinto g-nodo di respondant.

    • Calcola p_n0 = posizione del massimo distinto g-nodo di respondant.

    • tuple_n = una tupla con tutte le posizioni del nodo da 0 a l_n0 inclusi.

    • Prepara exclude_tuple_list = [] una lista di istanze di tuple globali nel g-nodo di ricerca di livello l_n0 + 1.

    • Per i da 0 a gsize[l_n0] - 1: Se ip_n0: Metti in exclude_tuple_list il g-nodo (l_n0, i), espresso come PeerTupleGNode di livello l_n0 nel g-nodo di livello l_n0 + 1.

    • Metti in exclude_tuple_list il nodo respondant, espresso come PeerTupleGNode di livello 0 nel g-nodo di livello l_n0 + 1.

    • While tdd.my_records_size() + tdd.tdh.retrieving_keys.size < tdd.max_records:

      • # la memoria destinata da n al servizio p non è esaurita.

      • Attendi qualche istante per non gravare sulle prestazioni della rete.
      • respondant = null. Sarà una tupla nel g-nodo di livello l_n0 + 1, poiché tuple_n ha l_n0 + 1 elementi.

      • Esegue ret = contact_peer(tdd.p_id, tuple_n, r, tdd.timeout_exec_send_keys, True, out respondant, exclude_tuple_list).

      • Il valore restituito dovrebbe essere un RequestSendKeysResponse, cioè una lista di Object. Altrimenti la risposta viene ignorata.

      • Se ret è una istanza di RequestSendKeysResponse:

        • Per ogni chiave k in ret:

          • Se tdd.is_valid_key(k):

            • Se not tdd.my_records_contains(k) e ktdd.tdh.removed_keys e not tdd.tdh.retrieving_keys.has_key(k):

              • # Non sa nulla di k.

              • Se dist(tdd.evaluate_hash_node(k, tuple_n.size), tuple_n) < dist(tdd.evaluate_hash_node(k, tuple_n.size), respondant):

                • Esegue ttl_db_retrieve_record(tdd, k). Cioè recupera il record per la chiave k.

                • Se tdd.my_records_size() + tdd.tdh.retrieving_keys.sizetdd.max_records: Esci dal ciclo for.

                • Attendi qualche istante per non gravare sulle prestazioni della rete.
        • Se tdd.my_records_size() + tdd.tdh.retrieving_keys.sizetdd.max_records: Esci dal ciclo while.

      • Metti in exclude_tuple_list il nodo respondant, espresso come PeerTupleGNode di livello 0 nel g-nodo di livello l_n0 + 1.

  • Se riceve PeersNoParticipantsInNetworkError:

    • # L'algoritmo termina.
  • Se riceve PeersRefuseExecutionError:

    • # L'algoritmo termina.

IPeersResponse ttl_db_got_request(ITemporalDatabaseDescriptor tdd, IPeersRequest r, int common_lvl) throws PeersRefuseExecutionError

  • Durante l'esecuzione del suo metodo exec, la classe PeerService del servizio p che ha ricevuto la richiesta r, può chiamare il metodo ttl_db_got_request per elaborare tale richiesta, posto che la richiesta rientri in uno di questi casi:

    • Una richiesta da cui si possa ottenere una unica chiave k; per tale chiave la richiesta r prevede la sola lettura o la sola sovrascrittura/inserimento/cancellazione o la lettura seguita dalla sovrascrittura/cancellazione.

    • Una istanza di RequestWaitThenSendRecord.

    • Una istanza di RequestSendKeys.

  • Gli argomenti del metodo ttl_db_got_request sono:

    • tdd: istanza di una classe che implementa ITemporalDatabaseDescriptor sopra descritta.

    • r: la richiesta.

    • common_lvl: il livello del minimo comune g-nodo con il richiedente, da 0 a levels compresi.

  • Al termine del metodo ttl_db_got_request viene restituita l'istanza di IPeersResponse che la classe PeerService del servizio p dovrà restituire al chiamante. Oppure viene lanciata l'eccezione PeersRefuseExecutionError prevista dal metodo exec di PeerService.

  • Se r è una istanza di RequestSendKeys:

    • # Chi riceve tale richiesta risponde subito con tutte le chiavi k dei record che detiene in memoria.

    • ret = new RequestSendKeysResponse().

    • Per ogni chiave Object k in tdd.get_all_keys():

      • Aggiungi k a ret.

    • Return ret.

    • # L'algoritmo termina.
  • Se r è una istanza di RequestWaitThenSendRecord:

    • # Chi riceve tale richiesta la tratta come una normale richiesta di sola lettura per la chiave k, a parte l'attesa.

    • Object k = r.k.

    • Se tdd.tdh.timer_non_exhaustive non è espirato:

      • Se tdd.my_records_contains(k):

        • Calcola δ il tempo critico di coerenza, basandosi sul numero approssimato di nodi nel minimo comune g-nodo tra il nodo corrente e quello del richiedente.

        • Attende δ.

        • Se tdd.my_records_contains(k):

          • ret = new RequestWaitThenSendRecordResponse().

          • ret.record = tdd.get_record_for_key(k).

          • Return ret.

          • # L'algoritmo termina.
        • Altrimenti:
          • Return new RequestWaitThenSendRecordNotFound(). Restituisce al richiedente l'eccezione NOT_FOUND.

          • # L'algoritmo termina.
      • Altrimenti-Se ktdd.tdh.removed_keys:

        • Return new RequestWaitThenSendRecordNotFound(). Restituisce al richiedente l'eccezione NOT_FOUND.

        • # L'algoritmo termina.
      • Altrimenti-Se ktdd.tdh.retrieving_keys.keys:

        • Lancia l'eccezione PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE. Rifiuta di elaborare r (perché non esaustivo).

        • # L'algoritmo termina.
      • Altrimenti:
        • # Non sa nulla di k.

        • Se tdd.my_records_size() + tdd.tdh.retrieving_keys.size < tdd.max_records, la memoria destinata da n al servizio p non è esaurita:

          • Mette un nuovo canale in tdd.tdh.retrieving_keys[k].

          • In una nuova tasklet:
            • Esegue ttl_db_retrieve_record(tdd, k). Cioè recupera il record per la chiave k.

        • tdd.tdh.timer_non_exhaustive = un nuovo timer che scade dopo tdd.msec_ttl millisecondi.

        • Lancia l'eccezione PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE. Rifiuta di elaborare r (perché non esaustivo).

        • # L'algoritmo termina.
    • Altrimenti:
      • # È esaustivo.

      • Svuota la lista tdd.tdh.removed_keys.

      • Se tdd.my_records_contains(k):

        • Calcola δ il tempo critico di coerenza, basandosi sul numero approssimato di nodi nel minimo comune g-nodo tra il nodo corrente e quello del richiedente.

        • Attende δ.

        • Se tdd.my_records_contains(k):

          • ret = new RequestWaitThenSendRecordResponse().

          • ret.record = tdd.get_record_for_key(k).

          • Return ret.

          • # L'algoritmo termina.
        • Altrimenti:
          • Return new RequestWaitThenSendRecordNotFound(). Restituisce al richiedente l'eccezione NOT_FOUND.

          • # L'algoritmo termina.
      • Altrimenti:
        • Return new RequestWaitThenSendRecordNotFound(). Restituisce al richiedente l'eccezione NOT_FOUND.

        • # L'algoritmo termina.
  • Object k = tdd.get_key_from_request(r).

  • Se tdd.tdh.timer_non_exhaustive non è espirato:

    • Se tdd.my_records_contains(k):

      • ret = tdd.execute_request(r). Elabora normalmente la richiesta r.

      • Se not tdd.my_records_contains(k):

        • Mette k in tdd.tdh.removed_keys.

      • Return ret.

      • # L'algoritmo termina.
    • Altrimenti-Se ktdd.tdh.removed_keys:

      • Se tdd.is_read_only_request(r) o tdd.is_read_write_request(r), cioè r è di lettura o lettura+scrittura:

        • Return tdd.prepare_response_not_found(r, k). Restituisce al richiedente l'eccezione NOT_FOUND.

        • # L'algoritmo termina.
      • Altrimenti:
        • # r è di scrittura:

        • Rimuove k da tdd.tdh.removed_keys.

        • Se tdd.my_records_size() + tdd.tdh.retrieving_keys.size < tdd.max_records, la memoria destinata da n al servizio p non è esaurita:

          • Return tdd.execute_request(r). Elabora normalmente la richiesta r, aggiungendo un record in memoria.

          • # L'algoritmo termina.
        • Altrimenti:
          • tdd.tdh.timer_non_exhaustive = un nuovo timer che scade dopo tdd.msec_ttl millisecondi.

          • Lancia l'eccezione PeersRefuseExecutionError.WRITE_OUT_OF_MEMORY. Rifiuta di elaborare r (perché out of memory).

          • # L'algoritmo termina.
    • Altrimenti-Se ktdd.tdh.retrieving_keys.keys:

      • Se tdd.is_read_only_request(r), cioè r è di sola lettura:

        • Lancia l'eccezione PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE. Rifiuta di elaborare r (perché non esaustivo).

        • # L'algoritmo termina.
      • Altrimenti:
        • # r è di scrittura o lettura+scrittura:

        • Attende di ricevere una comunicazione dal canale tdd.tdh.retrieving_keys[k].

        • Se tdd.is_read_write_request(r), cioè r prevede una lettura prima della scrittura:

          • Se not tdd.my_records_contains(k):

            • # Il nodo non ha il record ancora.
            • Mette k in tdd.tdh.removed_keys.

            • Return tdd.prepare_response_not_found(r, k). Restituisce al richiedente l'eccezione NOT_FOUND.

            • # L'algoritmo termina.
          • Altrimenti:
            • Return tdd.execute_request(r). Elabora normalmente la richiesta r.

            • # L'algoritmo termina.
        • Altrimenti:
          • Return tdd.execute_request(r). Elabora normalmente la richiesta r.

          • # L'algoritmo termina.
    • Altrimenti:
      • # Non sa nulla di k.

      • Se tdd.my_records_size() + tdd.tdh.retrieving_keys.size < tdd.max_records, la memoria destinata da n al servizio p non è esaurita:

        • Mette un nuovo canale in tdd.tdh.retrieving_keys[k].

        • In una nuova tasklet:
          • Esegue ttl_db_retrieve_record(tdd, k). Cioè recupera il record per la chiave k.

      • tdd.tdh.timer_non_exhaustive = un nuovo timer che scade dopo tdd.msec_ttl millisecondi.

      • Lancia l'eccezione PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE. Rifiuta di elaborare r (perché non esaustivo).

      • # L'algoritmo termina.
  • Altrimenti:
    • # È esaustivo.

    • Svuota la lista tdd.tdh.removed_keys.

    • Se tdd.my_records_contains(k):

      • Return tdd.execute_request(r). Elabora normalmente la richiesta r.

      • # L'algoritmo termina.
    • Altrimenti:
      • Se tdd.is_read_only_request(r) o tdd.is_read_write_request(r), cioè r è di lettura o lettura+scrittura:

        • Return tdd.prepare_response_not_found(r, k). Restituisce al richiedente l'eccezione NOT_FOUND.

        • # L'algoritmo termina.
      • Altrimenti:
        • # r è di scrittura:

        • Se tdd.my_records_size() + tdd.tdh.retrieving_keys.size < tdd.max_records, la memoria destinata da n al servizio p non è esaurita:

          • Return tdd.execute_request(r). Elabora normalmente la richiesta r, aggiungendo un record in memoria.

          • # L'algoritmo termina.
        • Altrimenti:
          • tdd.tdh.timer_non_exhaustive = un nuovo timer che scade dopo tdd.msec_ttl millisecondi.

          • Lancia l'eccezione PeersRefuseExecutionError.WRITE_OUT_OF_MEMORY. Rifiuta di elaborare r (perché out of memory).

          • # L'algoritmo termina.

internal void ttl_db_retrieve_record(ITemporalDatabaseDescriptor tdd, Object k)

  • Questo metodo è interno al modulo. Viene chiamato dai metodi sopra descritti (begin e got_request) per recuperare il record per la chiave k.

  • Gli argomenti del metodo ttl_db_retrieve_record sono:

    • tdd: istanza di una classe che implementa ITemporalDatabaseDescriptor sopra descritta.

    • k: la chiave.

  • Al termine del metodo ttl_db_retrieve_record il record è stato recuperato e messo in memoria, oppure la chiave è stata messa nell'elenco di chiavi da considerare definitivamente assenti anche in caso di nodo non esaustivo.

  • IPeersRequest r = new RequestWaitThenSendRecord(k).

  • Try:
    • PeerTupleNode respondant = null.

    • Esegue IPeersResponse ret = contact_peer(tdd.p_id, tdd.evaluate_hash_node(k, levels), r, tdd.timeout_exec_wait_then_send_record, True, out respondant).

    • Se ret è una istanza di RequestWaitThenSendRecordNotFound:

      • Se ktdd.tdh.removed_keys:

        • Aggiunge k a tdd.tdh.removed_keys.

      • INtkdChannel temp_ch = tdd.tdh.retrieving_keys[k].

      • tdd.tdh.retrieving_keys.unset(k). Rimuove k da tdd.tdh.retrieving_keys.

      • Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su temp_ch.

    • Altrimenti-Se ret è una istanza di RequestWaitThenSendRecordResponse:

      • Esegue tdd.set_record_for_key(k, ret.record). Scrive il record nella sua memoria.

      • Se ktdd.tdh.removed_keys:

        • Rimuove k da tdd.tdh.removed_keys.

      • INtkdChannel temp_ch = tdd.tdh.retrieving_keys[k].

      • tdd.tdh.retrieving_keys.unset(k). Rimuove k da tdd.tdh.retrieving_keys.

      • Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su temp_ch.

  • Se riceve PeersNoParticipantsInNetworkError:

    • Se ktdd.tdh.removed_keys:

      • Aggiunge k a tdd.tdh.removed_keys.

    • INtkdChannel temp_ch = tdd.tdh.retrieving_keys[k].

    • tdd.tdh.retrieving_keys.unset(k). Rimuove k da tdd.tdh.retrieving_keys.

    • Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su temp_ch.

  • Se riceve PeersRefuseExecutionError:

    • Se ktdd.tdh.removed_keys:

      • Aggiunge k a tdd.tdh.removed_keys.

    • INtkdChannel temp_ch = tdd.tdh.retrieving_keys[k].

    • tdd.tdh.retrieving_keys.unset(k). Rimuove k da tdd.tdh.retrieving_keys.

    • Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su temp_ch.

servizi con un numero preciso ed esiguo di possibili chiavi

I metodi fixed_keys_db_* servono a gestire un database distribuito in cui le possibili chiavi sono in un set definito e in numero esiguo. Il modulo, oltre a fornire tali metodi, fornisce la classe FixedKeysDatabaseHandler, l'interfaccia IFixedKeysDatabaseDescriptor.

La classe RequestNop è una classe serializzabile, che deriva da Object, e non ha alcun dato al suo interno. Implementa l'interfaccia (vuota) IPeersRequest. È una finta richiesta, serve solo a contattare il detentore corrente del record di una chiave e vederne l'indirizzo. Tale classe viene esposta dal modulo, in quanto la classe del servizio deve poter riconoscere una sua istanza, ma i suoi membri non sono esposti, incluso il costruttore.

La classe RequestNopResponse è una classe serializzabile, che deriva da Object, e non ha alcun dato al suo interno. Implementa l'interfaccia (vuota) IPeersResponse. È la risposta alla finta richiesta di cui sopra. Tale classe non viene esposta dal modulo.

La classe FixedKeysDatabaseHandler è esposta dal modulo, ma il suo contenuto è del tutto oscuro all'esterno del modulo. I suoi membri, accessibili solo dal modulo, sono:

  • internal FixedKeysDatabaseHandler: lo stesso costruttore della classe è accessibile solo dal modulo.

  • internal List<Object> not_started_keys: una lista di chiavi di cui non abbiamo avviato il recupero.

  • internal HashMap<Object,INtkdChannel> retrieving_keys: una mappa che associa ad una chiave un canale di comunicazione tra tasklet se per tale chiave è in corso una operazione di recupero.

L'interfaccia IFixedKeysDatabaseDescriptor espone questi metodi:

  • FixedKeysDatabaseHandler fkdh: una proprietà destinata ad essere popolata ed acceduta dal modulo con una istanza di FixedKeysDatabaseHandler.

  • int p_id: una proprietà che dice l'identificativo di questo servizio.

  • int timeout_exec_nop: una proprietà che dice il tempo massimo di esecuzione per la richiesta RequestNop per questo servizio.

  • int timeout_exec_wait_then_send_record: una proprietà che dice il tempo massimo di esecuzione per la richiesta RequestWaitThenSendRecord per questo servizio.

  • List<int> evaluate_hash_node(Object k, int lvl): una funzione che calcola la tupla hp(k) di questo servizio per la chiave k tenendo in considerazione che servono solo le posizioni interne al livello lvl.

  • bool key_equal_data(Object k1, Object k2) e uint key_hash_data(Object k): metodi le cui firme sono adatte per i delegati Gee.EqualDataFunc<Object> e Gee.HashDataFunc<Object>, per costruire una HashMap o una lista "con funzionalità di ricerca" di chiavi del servizio.

  • Object get_key_from_request(IPeersRequest r): una funzione che partendo dalla richiesta r, la quale non deve essere una istanza di RequestWaitThenSendRecord, restituisce la chiave k a cui si riferisce.

  • bool is_read_only_request(IPeersRequest r): una funzione che dice se la richiesta è di sola lettura.

  • IPeersResponse execute_request(IPeersRequest r): una funzione che elabora la richiesta r, la quale non deve essere una istanza di RequestWaitThenSendRecord, e produce una istanza di IPeersResponse che possa essere usata come valore di ritorno.

  • Object get_record_for_key(Object k): una funzione che restituisce il record per la chiave k, avendo come requisito (cioè il chiamante deve averlo già verificato) che tale chiave è presente nella memoria.

  • void set_record_for_key(Object k, Object rec): una funzione che mette in memoria il record rec per la chiave k. Questa funzione deve garantire di essere atomica, cioè di non schedulare altre tasklet.

  • void set_record_to_default_for_key(Object k): una funzione che mette in memoria un adeguato valore di default per la chiave k. Questa funzione deve garantire di essere atomica, cioè di non schedulare altre tasklet.

void fixed_keys_db_begin(IFixedKeysDatabaseDescriptor fkdd, List<Object> K)

  • Viene avviata in una tasklet dalla classe del servizio, che deriva PeerService.

  • Se si tratta di un servizio opzionale, viene chiamata solo dopo che sono state reperite con successo le mappe dei partecipanti ai servizi opzionali.
  • Gli argomenti del metodo fixed_keys_db_begin sono:

    • K: la lista di tutte le possibili chiavi.

    • tuple_n: una tupla con tutte le posizioni del nodo da 0 a levels - 1.

  • fkdd.fkdh = new FixedKeysDatabaseHandler().

  • fkdd.fkdh.not_started_keys = new ArrayList<Object>(fkdd.key_equal_data).

  • fkdd.fkdh.retrieving_keys = new HashMap<Object,INtkdChannel>(fkdd.key_equal_data, fkdd.key_hash_data).

  • Per ogni chiave k nell'insieme completo K:

    • Aggiunge k a fkdd.fkdh.not_started_keys.

  • Per ogni chiave k nell'insieme completo K:

    • # Sia respondant il detentore corrente di k. Lo trova con una chiamata a contact_peer in cui non chiede nulla.

    • PeerTupleNode respondant = null. Sarà una tupla nel g-nodo di livello levels, poiché tuple_n ha levels elementi.

    • IPeersRequest r = new RequestNop().

    • Esegue IPeersResponse ret = contact_peer(fkdd.p_id, tuple_n, r, fkdd.timeout_exec_nop, True, out respondant).

    • Se dist(fkdd.evaluate_hash_node(k, tuple_n.size), tuple_n) < dist(fkdd.evaluate_hash_node(k, tuple_n.size), respondant):

      • Mette un nuovo canale in fkdd.fkdh.retrieving_keys[k].

      • In una nuova tasklet:
        • Esegue fixed_keys_db_retrieve_record(fkdd, k). Cioè recupera il record per la chiave k.

      • Rimuove k da fkdd.fkdh.not_started_keys.

      • Attendi qualche istante per non gravare sulle prestazioni della rete.
    • Altrimenti:
      • Rimuove k da fkdd.fkdh.not_started_keys.

IPeersResponse fixed_keys_db_got_request(IFixedKeysDatabaseDescriptor fkdd, IPeersRequest r, int common_lvl) throws PeersRefuseExecutionError

  • Durante l'esecuzione del suo metodo exec, la classe PeerService del servizio p che ha ricevuto la richiesta r, può chiamare il metodo fixed_keys_db_got_request per elaborare tale richiesta, posto che la richiesta rientri in uno di questi casi:

    • Una richiesta da cui si possa ottenere una unica chiave k; per tale chiave la richiesta r prevede la sola lettura o la sola scrittura o la lettura seguita dalla scrittura.

    • Una istanza di RequestWaitThenSendRecord.

    • Una istanza di RequestNop.

  • Gli argomenti del metodo fixed_keys_db_got_request sono:

    • fkdd: istanza di una classe che implementa IFixedKeysDatabaseDescriptor sopra descritta.

    • r: la richiesta.

    • common_lvl: il livello del minimo comune g-nodo con il richiedente, da 0 a levels compresi.

  • Al termine del metodo fixed_keys_db_got_request viene restituita l'istanza di IPeersResponse che la classe PeerService del servizio p dovrà restituire al chiamante. Oppure viene lanciata l'eccezione PeersRefuseExecutionError prevista dal metodo exec di PeerService.

  • Se r è una istanza di RequestWaitThenSendRecord:

    • # Chi riceve tale richiesta la tratta come una normale richiesta di sola lettura per la chiave k, a parte l'attesa.

    • Object k = r.k.

    • Se kfkdd.fkdh.not_started_keys:

      • Lancia l'eccezione PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE. Rifiuta di elaborare r (perché non esaustivo).

      • # L'algoritmo termina.
    • Altrimenti-Se kfkdd.fkdh.retrieving_keys.keys:

      • Lancia l'eccezione PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE. Rifiuta di elaborare r (perché non esaustivo).

      • # L'algoritmo termina.
    • Altrimenti:
      • Calcola δ il tempo critico di coerenza, basandosi sul numero approssimato di nodi nel minimo comune g-nodo tra il nodo corrente e quello del richiedente.

      • Attende δ.

      • ret = new RequestWaitThenSendRecordResponse().

      • ret.record = fkdd.get_record_for_key(k).

      • Return ret.

      • # L'algoritmo termina.
  • Se r è una istanza di RequestNop:

    • # Chi riceve tale richiesta risponde subito, anche se fosse non esaustivo.

    • Return new RequestNopResponse().

    • # L'algoritmo termina.
  • Object k = fkdd.get_key_from_request(r).

  • Se kfkdd.fkdh.not_started_keys:

    • Lancia l'eccezione PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE. Rifiuta di elaborare r (perché non esaustivo).

    • # L'algoritmo termina.
  • Altrimenti-Se kfkdd.fkdh.retrieving_keys.keys:

    • Se fkdd.is_read_only_request(r), cioè r è di sola lettura:

      • Lancia l'eccezione PeersRefuseExecutionError.READ_NOT_FOUND_NOT_EXHAUSTIVE. Rifiuta di elaborare r (perché non esaustivo).

      • # L'algoritmo termina.
    • Altrimenti:
      • # r è di scrittura:

      • Attende di ricevere una comunicazione dal canale fkdd.fkdh.retrieving_keys[k].

      • Return fkdd.execute_request(r). Elabora normalmente la richiesta r.

      • # L'algoritmo termina.
  • Altrimenti:
    • Return fkdd.execute_request(r). Elabora normalmente la richiesta r.

    • # L'algoritmo termina.

internal void fixed_keys_db_retrieve_record(IFixedKeysDatabaseDescriptor fkdd, Object k)

  • Questo metodo è interno al modulo. Viene chiamato dai metodi sopra descritti (begin e got_request) per recuperare il record per la chiave k.

  • Gli argomenti del metodo fixed_keys_db_retrieve_record sono:

    • fkdd: istanza di una classe che implementa IFixedKeysDatabaseDescriptor sopra descritta.

    • k: la chiave.

  • Al termine del metodo fixed_keys_db_retrieve_record il record è stato recuperato e messo in memoria, oppure la memoria è stata valorizzata con un apposito default.

  • IPeersRequest r = new RequestWaitThenSendRecord(k).

  • Try:
    • Esegue IPeersResponse ret = contact_peer(fkdd.p_id, fkdd.evaluate_hash_node(k, levels), r, fkdd.timeout_exec_wait_then_send_record, True, out respondant).

    • Se ret è una istanza di RequestWaitThenSendRecordResponse:

      • Esegue fkdd.set_record_for_key(k, ret.record). Scrive il record nella sua memoria.

    • Altrimenti:
      • Esegue fkdd.set_record_to_default_for_key(k). Valorizza la sua memoria con un appropriato default.

  • Se riceve PeersNoParticipantsInNetworkError:

    • Esegue fkdd.set_record_to_default_for_key(k). Valorizza la sua memoria con un appropriato default.

  • Se riceve PeersRefuseExecutionError:

    • Esegue fkdd.set_record_to_default_for_key(k). Valorizza la sua memoria con un appropriato default.

  • INtkdChannel temp_ch = fkdd.fkdh.retrieving_keys[k].

  • fkdd.fkdh.retrieving_keys.unset(k). Rimuove k da fkdd.fkdh.retrieving_keys.

  • Invia un messaggio asincrono (senza schedulare altre tasklet) a tutti quelli che sono in attesa su temp_ch.

Netsukuku/ita/docs/ModuloPeers/AppuntiAlgo2 (last edited 2015-11-28 11:14:43 by lukisi)