Differences between revisions 40 and 41
Revision 40 as of 2015-10-15 10:09:26
Size: 43430
Editor: lukisi
Comment:
Revision 41 as of 2015-10-25 07:43:52
Size: 8379
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 114: Line 114:
=== per assicurare delle repliche === === Per migliorare la persistenza dei dati ===
Line 135: Line 135:
 * Se si riceve l'eccezione !PeersNoParticipantsInNetworkError o !PeersRefuseExecutionError:
  * Return False.
 * Se si riceve l'eccezione !PeersNoParticipantsInNetworkError o !PeersDatabaseError:
  * Return False. L'eccezione !PeersDatabaseError va vista come "OUT_OF_MEMORY" essendo la replica una operazione di sovrascrittura o inserimento.
Line 142: Line 142:
=== 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''.
=== Per garantire la coerenza dei dati ===
Per garantire la coerenza dei dati e gestire l'esaustività e le limitazioni di memoria dei singoli nodi, gli algoritmi sviluppati sono illustrati nei seguenti documenti:
 * [[Netsukuku/ita/docs/ModuloPeers/DatabaseTTL|Database con un Time To Live]]
 * [[Netsukuku/ita/docs/ModuloPeers/DatabaseFixedKeys|Database a chiavi fisse]]

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 migliorare la persistenza dei dati

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 PeersDatabaseError:

    • Return False. L'eccezione PeersDatabaseError va vista come "OUT_OF_MEMORY" essendo la replica una operazione di sovrascrittura o inserimento.

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

Per garantire la coerenza dei dati

Per garantire la coerenza dei dati e gestire l'esaustività e le limitazioni di memoria dei singoli nodi, gli algoritmi sviluppati sono illustrati nei seguenti documenti:

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