Differences between revisions 5 and 7 (spanning 2 versions)
Revision 5 as of 2015-09-16 14:15:20
Size: 15909
Editor: lukisi
Comment:
Revision 7 as of 2015-12-02 09:12:54
Size: 19105
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 7: Line 7:
Quando il nodo ha completato la fase di bootstrap, il nodo istanzia il suo !CoordinatorManager passando al costruttore: Appena il nodo entra in una rete o ne costituisce una nuova, istanzia il suo !CoordinatorManager passando al costruttore:
 * ''level_new_gnode''. Il livello del g-nodo che ha costituito.

Quando il nodo ha completato la fase di bootstrap del modulo QSPN, il nodo informa il suo !CoordinatorManager chiamando il metodo ''bootstrap_completed'' a cui passa:
Line 11: Line 14:
Quando il nodo considera che la notifica della sua presenza abbia ormai raggiunto tramite ETP tutti i membri del g-nodo in cui è entrato, l'utilizzatore del modulo chiama il metodo ''presence_notified'' del !CoordinatorManager. Prima di questo evento il nodo non è in grado di eseguire i metodi remoti chiamati da un suo vicino. Questa impossibilità viene segnalata con l'eccezione !CoordinatorNodeNotReadyError.
Line 14: Line 17:
Nel costruttore del !CoordinatorManager viene creata una istanza di !CoordinatorService (che descriveremo sotto) e viene registrata nel !PeersManager. Quando viene chiamato il suo metodo ''bootstrap_completed'', il !CoordinatorManager crea una istanza di !CoordinatorService (che descriveremo sotto) e la registra nel !PeersManager.
Line 19: Line 22:
Il metodo ha come argomento lo stub per contattare il vicino. Prevede l'eccezione !StubNotWorkingError. Il metodo ha come argomento lo stub per contattare il vicino. Prevede le eccezioni !CoordinatorStubNotWorkingError e !CoordinatorNodeNotReadyError.
Line 26: Line 29:
Il metodo ha come argomento lo stub per contattare il vicino e il livello a cui fare richiesta. Prevede l'eccezione !StubNotWorkingError e l'eccezione !SaturatedGnodeError. Il metodo ha come argomento lo stub per contattare il vicino e il livello a cui fare richiesta. Prevede le eccezioni !CoordinatorStubNotWorkingError, !CoordinatorNodeNotReadyError, !CoordinatorInvalidLevelError e !CoordinatorSaturatedGnodeError.
Line 31: Line 34:
Quando il modulo vuole chiedere ad un vicino informazioni sui posti liberi nei suoi g-nodi usa il metodo remoto ''retrieve_neighbor_map''. Non ha argomenti. Non prevede eccezioni, oltre alle solite !StubError e !DeserializeError. Restituisce una istanza di ICoordinatorNeighborMapMessage. Quando il modulo vuole chiedere ad un vicino informazioni sui posti liberi nei suoi g-nodi usa il metodo remoto ''retrieve_neighbor_map''. Non ha argomenti. Prevede l'eccezione !CoordinatorNodeNotReadyError, oltre alle solite !StubError e !DeserializeError. Restituisce una istanza di ICoordinatorNeighborMapMessage.
Line 36: Line 39:
Quando il modulo vuole chiedere ad un vicino di prenotare per lui un posto nel suo g-nodo di livello ''l'' usa il metodo remoto ''ask_reservation''. Ha come argomento il livello ''l''. Prevede l'eccezione !SaturatedGnodeError, oltre alle solite !StubError e !DeserializeError. Restituisce una istanza di ICoordinatorReservationMessage. Quando il modulo vuole chiedere ad un vicino di prenotare per lui un posto nel suo g-nodo di livello ''l'' usa il metodo remoto ''ask_reservation''. Ha come argomento il livello ''l''. Prevede le eccezioni !CoordinatorNodeNotReadyError, !CoordinatorInvalidLevelError e !CoordinatorSaturatedGnodeError, oltre alle solite !StubError e !DeserializeError. Restituisce una istanza di ICoordinatorReservationMessage.
Line 42: Line 45:
Il modulo deriva da !PeerService la classe !CoordinatorService.

Questa classe è interna al modulo. Contiene questi membri:
Il modulo deriva da !PeerService la classe !CoordinatorService. All'interno di tale classe (per poter accedere ai suoi membri privati) definisce anche la classe !DatabaseDescriptor che implementa l'interfaccia IFixedKeysDatabaseDescriptor.

La classe !CoordinatorService è interna al modulo. Contiene questi membri:
Line 47: Line 50:
 * ''retrieve_cache_done''. Booleano, inizializzato a ''False''.
 * ''my_presence_should_be_known''. Booleano, inizializzato a ''False''.
 * ''bookings''. Lista di liste di Booking. Memoria delle prenotazioni. Contiene ''levels'' elementi. L'elemento ''i''-esimo è la lista di prenotazioni attive per il nostro g-nodo di livello ''i''+1. La classe Booking ha una posizione (int ''pos'') e un time-to-live (Timer ''ttl''). E' una classe serializzabile e interna al modulo.
 * ''max_eldership''. Lista di int. Contiene ''levels'' elementi. L'elemento ''i''-esimo dice il valore più alto di progressione già assegnato ad una prenotazione per un nuovo g-nodo di livello ''i'' dentro il nostro g-nodo di livello ''i''+1. Un valore più alto significa che il g-nodo è arrivato dopo, cioè esso è più giovane.

Quando viene chiamato il metodo ''presence_notified'' del !CoordinatorManager, la classe !CoordinatorService fa queste operazioni:
 * ''level_new_gnode'' = ''peers_manager.level_new_gnode''.
 * Se ''level_new_gnode'' ≠ ''levels'':
  * In una nuova tasklet:
   * Avvia le operazioni di recupero dei record di sua pertinenza. Trattandosi di un servizio non opzionale, non attende l'evento ''!PeersManager.participant_maps_ready''.
   * __'''TODO'''__ rivedere tutto.
   * Costruisce la richiesta ''r'' di tipo "retrieve_cache" dal livello ''level_new_gnode''+1 in su.
   * Avvia il primo recupero con il metodo ''!PeersManager.begin_retrieve_cache''
   . Attende l'esito, cioè il booleano restituito, la risposta ''resp'' da interpretare, l'oggetto IPeersContinuation ''cont'' da passare nelle chiamate successive.
   * Se ''resp'' ≠ ''null'':
    * Si accerta che ''resp.bookings.size'' = ''resp.max_eldership.size'' = ''levels - level_new_gnode''. Altrimenti emette un warning e ignora questa risposta.
    * Per ''i'' da 0 a ''resp.bookings.size-1'':
     * ''j'' = ''level_new_gnode + i''.
     * Per ogni Booking ''b'' ∈ ''resp.bookings[i]'':
      * Se ''b'' ∉ ''bookings[j]'':
       * Aggiungi ''b'' a ''bookings[j]''.
     * Se ''resp.max_eldership[i]'' > ''max_eldership[j]'':
      * ''max_eldership[j]'' = ''resp.max_eldership[i]''.
   * Se l'esito è ''True'':
    * While True:
     * Avvia un altro recupero con il metodo ''!PeersManager.next_retrieve_cache'' passando la IPeersContinuation ''cont''.
     . Attende l'esito, cioè il booleano restituito e la risposta ''resp'' da interpretare.
     * Se ''resp'' ≠ ''null'':
      * In base a ''resp'' modifica i propri membri ''bookings'' e ''max_eldership'', come sopra.
     * Se l'esito è ''False'':
      * Esci dal ciclo.
 * Imposta il suo flag ''retrieve_cache_started''.

Il metodo ''is_ready()'' della classe !CoordinatorService si basa sul flag ''retrieve_cache_started''.
 * ''fkdd''. Contiene una istanza di !DatabaseDescriptor valorizzata nel costruttore.
 * ''booking_lists[i]'', con ''i'' da 0 a ''levels'' - 1. Lista di Booking. Memoria delle prenotazioni. La lista di prenotazioni attive per il nostro g-nodo di livello ''i'' + 1. Il costruttore la crea vuota.
 . La classe Booking ha una posizione (int ''pos'') e un time-to-live (Timer ''ttl''). E' una classe serializzabile e interna al modulo.
 * ''max_elderships[i]'', con ''i'' da 0 a ''levels'' - 1. Intero. Il valore più alto di progressione già assegnato ad una prenotazione per un nuovo g-nodo di livello ''i'' dentro il nostro g-nodo di livello ''i'' + 1. Il costruttore lo inizializza a 0.
 . Quando assegnato ad un g-nodo, un valore di anzianità più alto significa che il g-nodo è arrivato dopo, cioè esso è più giovane.

Nel costruttore, dopo aver valorizzato i membri nel modo suddetto, viene fatta la registrazione sul !PeersManager. Dopo, in una tasklet, si avvia il metodo ''fixed_keys_db_on_startup'' del !PeersManager passando l'istanza ''fkdd'' e il livello del nuovo g-nodo costituito (che viene passato al costruttore). Questo per avvalersi degli algoritmi di gestione delle problematiche di mantenimento di un database a chiavi fisse.

Quando viene chiamato il metodo ''get_record_for_key'' dell'istanza ''fkdd'', la classe !CoordinatorService fa queste operazioni:
 * Assert: La chiave ''k'' passata è una istanza valida di !CoordinatorKey.
 * Valorizza e restituisce una istanza di !CoordinatorRecord con:
  * ''lvl'': lo stesso livello che è nella chiave: ''k.lvl''.
  * ''booking_list'': una copia della lista nel membro ''booking_lists[lvl - 1]'' del !CoordinatorService.
  * ''max_eldership'': l'intero che è nel membro ''max_elderships[lvl - 1]'' del !CoordinatorService.

Quando viene chiamato il metodo ''set_record_for_key'' dell'istanza ''fkdd'', la classe !CoordinatorService fa queste operazioni:
 * Assert: La chiave ''k'' passata è una istanza valida di !CoordinatorKey.
 * Assert: Il record ''rec'' passato è una istanza valida di !CoordinatorRecord.
 * ''booking_lists[rec.lvl - 1]'' = una copia della lista in ''rec.booking_list''.
 * ''max_elderships[rec.lvl - 1]'' = ''rec.max_eldership''.

==== Richieste fornite dal servizio ====
Ricordiamo che quando una richiesta ad un servizio peer-to-peer viene ricevuta dall'hash_node viene chiamato il metodo ''exec'' della classe che deriva la !PeerService, nel nostro caso la classe !CoordinatorService. Questo metodo riceve una istanza di IPeersRequest (e la tupla che identifica il chiamante, ma in questo caso non la utiliziamo) e deve restituire una istanza di IPeersResponse.

Il metodo ''exec'' della classe !CoordinatorService, per avvalersi degli algoritmi di gestione delle problematiche di mantenimento di un database a chiavi fisse, chiama il metodo ''fixed_keys_db_on_request'' del !PeersManager passando l'istanza ''fkdd''. Questo metodo del modulo !PeerServices restituisce il controllo al modulo Coordinator richiamando sull'istanza ''fkdd'' il metodo ''execute'' definito così dall'interfaccia IFixedKeysDatabaseDescriptor:
 * ''IPeersResponse execute(IPeersRequest r) throws !PeersRefuseExecutionError, !PeersRedoFromStartError''
Line 83: Line 78:
Ricordiamo che quando una richiesta ad un servizio peer-to-peer viene ricevuta dall'hash_node viene chiamato il metodo ''exec'' della classe che deriva la !PeerService. Questa riceve una istanza di IPeersRequest e deve restituire una istanza di IPeersResponse.

Se il metodo ''exec'' della classe !CoordinatorService viene chiamato con una istanza di !CoordinatorRequest (si veda sotto) che ha il membro ''name'' = 'reserve' allora abbiamo ricevuto una richiesta di "prenota un posto nel tuo g-nodo di livello ''lvl''". Le operazioni da fare sono:
 * Se ci sono problemi a deserializzare gli argomenti viene fatta pervernire una eccezione !DeserializeError. Inclusi errori nei valori accettabili di ''lvl''.
Se il metodo ''execute'' della classe !CoordinatorService.!DatabaseDescriptor viene chiamato con una istanza di !CoordinatorReserveRequest (si veda sotto) allora abbiamo ricevuto una richiesta di "prenota un posto nel tuo g-nodo di livello ''lvl''". Le operazioni da fare sono:
 * Se l'argomento ''lvl'' ha un valore non accettabile viene fatta pervernire una eccezione !CoordinatorInvalidLevelError. Lo si fa valorizzando appositamente i campi ''error_*'' di una nuova istanza di !CoordinatorReserveResponse che viene restituita.
Line 88: Line 81:
 * Verifica nella mappa ''map'' del !CoordinatorManager quali posizioni sono libere nel livello ''lvl''. Se nessuna posizione risulta libera, lancia una eccezione.
 * Dalla lista ottenuta di posizioni libere, esclude quelle presenti nelle prenotazioni ''bookings(lvl-1)'' del !CoordinatorService. Nello scorrere questa lista, rimuove le istanze di Booking che sono scadute (membro ''ttl''). Se nessuna posizione risulta libera, lancia una eccezione.
 * Verifica nella mappa ''map'' del !CoordinatorManager quali posizioni sono libere nel livello ''lvl''. Se nessuna posizione risulta libera, lancia una eccezione !CoordinatorSaturatedGnodeError (campi ''error_*'' di !CoordinatorReserveResponse).
 * Dalla lista ottenuta di posizioni libere, esclude quelle presenti nelle prenotazioni ''booking_lists[lvl - 1]'' del !CoordinatorService. Nello scorrere questa lista, rimuove le istanze di Booking che sono scadute (membro ''ttl''). Se nessuna posizione risulta libera, lancia una eccezione !CoordinatorSaturatedGnodeError (campi ''error_*'' di !CoordinatorReserveResponse).
Line 91: Line 84:
 * Segnala la prenotazione nella memoria. Cioè crea una istanza di Booking ''pos'' con ''ttl'' a 20 secondi (una {{{private const}}} nel codice) nella lista ''bookings(lvl-1)'' del !CoordinatorService.
 * Incrementa di 1 la ''max_eldership(lvl-1)'' nella memoria.
 * Con i dati in memoria prepara la risposta ''ret'' (pos=pos, eldership=max_eldership(lvl-1))
 * Segnala la prenotazione nella memoria. Cioè crea una istanza di Booking ''pos'' con ''ttl'' a 60 secondi (una {{{private const}}} nel codice) nella lista ''booking_lists[lvl - 1]'' del !CoordinatorService.
 * Incrementa di 1 la ''max_elderships[lvl - 1]'' nella memoria.
 * Con i dati in memoria prepara la risposta ''ret'', una istanza di !CoordinatorReserveResponse con:
  * pos
= pos.
  *
eldership = max_elderships[lvl - 1].
Line 98: Line 93:
  . Costruisce la richiesta ''r'' di tipo 'replica_reserve' per la posizione ''pos'' con anzianità ''eldership'' dentro il nostro g-nodo di livello ''lvl''.   . Costruisce la richiesta ''r'' di tipo 'replica_record' il cui contenuto si ottiene con il metodo ''get_record_for_key'' di ''fkdd''.
Line 116: Line 111:
 * Completa ''ret'' con (elderships=anzianità dei miei g-nodi da ''lvl'' in su)
 * Restituisce ''ret''.

==== Metodo replica_reserve ====
Se il metodo ''exec'' della classe !CoordinatorService viene chiamato con una istanza di !CoordinatorRequest (si veda sotto) che ha il membro ''name'' = 'replica_reserve' allora abbiamo ricevuto una richiesta di "replica per una prenotazione del posto ''pos'' con anzianità ''eldership'' nel nostro g-nodo di livello ''lvl''". Le operazioni da fare sono:
 * Se ci sono problemi a deserializzare gli argomenti viene fatta pervernire una eccezione !DeserializeError. Inclusi errori nei valori accettabili di ''lvl'' e ''pos''.
 * Atomic on.
 * Segnala la prenotazione nella memoria. Cioè crea una istanza di Booking ''pos'' nella lista ''bookings(lvl-1)'' del !CoordinatorService.
 * Se ''max_eldership(lvl-1)'' < ''eldership'':
  * Imposta ''max_eldership(lvl-1)'' = ''eldership''.
 * Atomic off.
 * Restituisce OK.

==== Metodo retrieve_cache ====
Se il metodo ''exec'' della classe !CoordinatorService viene chiamato con una istanza di !CoordinatorRequest (si veda sotto) che ha il membro ''name'' = 'retrieve_cache' allora abbiamo ricevuto una richiesta di "recupera i record delle prenotazioni dal livello ''lvl'' in su", con 0 < ''lvl'' ≤ ''levels''. Le operazioni da fare sono:
 * Se ci sono problemi a deserializzare gli argomenti viene fatta pervernire una eccezione !DeserializeError. Inclusi errori nei valori accettabili di ''lvl''.
 * Prepara ''ret'' con gli elementi da ''lvl''-1 in su della lista ''bookings'' di !CoordinatorService e con gli elementi da ''lvl''-1 in su della lista ''max_eldership'' di !CoordinatorService.
Line 138: Line 116:
Ridefinisce il suo metodo ''perfect_tuple''. La chiave che può essere usata per calcolare l'hash_node è un intero ''l'' da 1 a ''levels''. La lista di interi restituita è composta da ''l'' volte il valore 0. Ridefinisce il suo metodo ''perfect_tuple''. La chiave che può essere usata per calcolare l'hash_node è un intero ''l'' da 1 a ''levels''. Si può generare la tupla inizialmente con l'implementazione di base del metodo ''perfect_tuple'' (quindi occorre definire il metodo ''hash_from_key''), ma poi la tupla restituita deve avere solo i primi ''l'' elementi.

==== Richieste fatte al servizio ====
Ricordiamo che questo servizio è non opzionale. Il nodo stesso che fa la richiesta è partecipante al servizio ed è sempre incluso nel g-nodo in cui la ricerca viene eventualmente circoscritta. Quindi l'eccezione !PeersNoParticipantsInNetworkError rilanciata dal metodo ''call'' della classe base !PeerClient può sempre essere trattata come un errore.

Ricordiamo inoltre che la gestione di un database a chiavi fisse è tale che un nodo può rifiutarsi di rispondere perché ''non esaustivo'' solo in operazioni di sola lettura. Nelle chiamate al metodo ''call'' della classe base !PeerClient fatte per richieste che non sono di sola lettura (ad esempio la richiesta 'reserve') l'eccezione !PeersDatabaseError può essere trattata come un errore.

==== Metodo reserve ====
Se il nodo corrente vuole prenotare un posto nel suo g-nodo di livello ''lvl'' fa al servizio Coordinator la richiesta !CoordinatorReserveRequest con la chiave ''lvl''. Per farlo chiama il metodo ''reserve'' nella classe !CoordinatorClient.

Il metodo prende a parametro il livello ''lvl''. Restituisce una istanza di ''Reservation''. Può rilanciare le eccezioni: !CoordinatorSaturatedGnodeError.

In caso di comportamento anomalo del nodo che risponde al servizio, il metodo della classe !CoordinatorClient rilancia una eccezione !CoordinatorSaturatedGnodeError. Non possiamo in ogni caso sapere se il nodo che risponde agisce in modo corretto; anche se sappiamo che non agisce correttamente non sappiamo se lo fa per errore o con malizia; non possiamo sapere se risponderà in modo palesemente errato anche alle richieste provenienti da altri nodi; in definitiva, cercare di porre rimedio diversamente sarebbe inutile.

Le operazioni da fare sono:
 * Verifica che il livello ''lvl'' sia nel range valido. Altrimenti il programma abortisce con un messaggio di errore.
 * Prepara la richiesta ''r'', il tempo ''timeout_exec'', la chiave ''k''.
 * ''IPeersResponse resp''.
 * ''Reservation ret''.
 * Try:
  * Richiama il metodo ''call'' della classe base. ''resp'' = ''call(k, r, timeout_exec)''.
 * Se riceve !PeersNoParticipantsInNetworkError:
  * Il programma abortisce con un messaggio di errore.
 * Se riceve !PeersDatabaseError:
  * Il programma abortisce con un messaggio di errore.
 * Se ''resp'' è una istanza di !CoordinatorReserveResponse:
  * Se ''resp.error_*'' non sono tutte a ''null'':
   * Se i campi error_* indicano un !CoordinatorSaturatedGnodeError:
    * Rilancia l'eccezione !CoordinatorSaturatedGnodeError con il messaggio ricevuto. Termina l'algoritmo.
   * Scrive un warning riportando l'eccezione indicata e il messaggio ricevuto.
   * Rilancia l'eccezione !CoordinatorSaturatedGnodeError riportando l'eccezione indicata e il messaggio ricevuto. Termina l'algoritmo.
  * Se ''resp.pos'' non è coerente con la topologia in ''lvl'' - 1, oppure è la mia stessa posizione in ''lvl'' - 1:
   * Scrive un warning.
   * Rilancia l'eccezione !CoordinatorSaturatedGnodeError. Termina l'algoritmo.
  * Se ''resp.eldership'' < 1:
   * Scrive un warning.
   * Rilancia l'eccezione !CoordinatorSaturatedGnodeError. Termina l'algoritmo.
  * Se ''resp.pos'' non è coerente con la topologia in ''lvl'' - 1:
   * Scrive un warning.
   * Rilancia l'eccezione !CoordinatorSaturatedGnodeError. Termina l'algoritmo.
  * Prepara ''ret'' una istanza di ''Reservation'' con:
   * ''levels'': map.get_levels().
   * ''gsizes'': una lista di ''levels'' elementi: gsizes[i] = map.get_gsize(i).
   * ''lvl'': lvl - 1.
   * ''pos'': resp.pos.
   * ''eldership'': resp.eldership.
   * ''upper_pos'': una lista di ''levels'' - ''lvl'' - 1 elementi: upper_pos[i] = map.get_my_pos(i+lvl+1).
   * ''upper_elderships'': una lista di ''levels'' - ''lvl'' - 1 elementi: upper_pos[i] = map.get_eldership(i+lvl+1).
 * Altrimenti:
  * # ''resp'' è una classe non prevista:
  * Scrive un warning.
  * Rilancia l'eccezione !CoordinatorSaturatedGnodeError. Termina l'algoritmo.
 * Restituisce ''ret''.
Line 141: Line 171:
Viene definita la classe interna !CoordinatorRequest. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersRequest. Serve per tutte le richieste che possono essere fatte al servizio. Tale classe contiene:
 * ''name''. Una stringa che identifica la richiesta. I valori possibili sono: ['reserve', 'retrieve_cache', 'replica_reserve'].
 * ''reserve_lvl''. Usato se ''name'' = 'reserve'. Un intero che indica il livello del g-nodo dentro il quale riservare un posto.
 * ''cache_from_lvl''. Usato se ''name'' = 'retrieve_cache'. Un intero che dice di passare i record da quel livello in su.
 * ''replica_lvl''. Usato se ''name'' = 'replica_reserve'. Un intero che dice il livello del g-nodo dentro il quale è stato prenotato un posto.
 * ''replica_pos''. Usato se ''name'' = 'replica_reserve'. Un intero che dice la posizione della prenotazione.
 * ''replica_eldership''. Usato se ''name'' = 'replica_reserve'. Un intero che dice l'anzianità del posto prenotato.

----
Viene definita la classe interna !CoordinatorReserveResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta solo per la richiesta 'reserve'. Tale classe contiene:
Viene definita la classe interna !CoordinatorKey. Si tratta di una classe serializzabile che deriva da Object. Serve per la rappresentare una chiave del servizio, cioè il livello del g-nodo da coordinare. Tale classe contiene:
 * ''lvl''. Un intero da 1 a ''levels'' che indica il livello del g-nodo da coordinare.

----
Viene definita la classe interna !CoordinatorRecord. Si tratta di una classe serializzabile che deriva da Object. Serve per la rappresentare un record del servizio, cioè la memoria condivisa del g-nodo da coordinare. Tale classe contiene:
 * ''lvl''. Un intero da 1 a ''levels'' che indica il livello del g-nodo da coordinare.
 * ''booking_list''. Lista di Booking. Memoria delle prenotazioni. La lista di prenotazioni attive per il nostro g-nodo di livello ''lvl''.
 * ''max_eldership''. Intero. Il valore più alto di progressione già assegnato ad una prenotazione per un nuovo g-nodo di livello ''lvl'' - 1 dentro il nostro g-nodo di livello ''lvl''.

----
Viene definita la classe interna !CoordinatorReserveRequest. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersRequest. Serve per la richiesta 'reserve'. Tale classe contiene:
 * ''lvl''. Un intero che indica il livello del g-nodo dentro il quale riservare un posto.

----
Viene definita la classe interna !CoordinatorReserveResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta per la richiesta !CoordinatorReserveRequest. Tale classe contiene:
Line 154: Line 189:
 * ''pos''. Un intero. La posizione assegnata.
 * ''elderships''. Una lista di interi. Le anzianità da ''l''-1 (del g-nodo appena assegnato) fino a ''levels''.

----
Viene definita la classe interna !CoordinatorReplicaReserveResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta solo per la richiesta 'replica_reserve'. Tale classe contiene:
 * ''error_domain''. Una stringa nullable.
 * ''error_code''. Una stringa nullable.
 * ''error_message''. Una stringa nullable. Queste sono tutte a ''null'' se l'esito è buono, mentre sono valorizzate se si vuole indicare una eccezione.

Non ha altri membri da valorizzare se l'esito della replica è positivo.

----
Viene definita la classe interna !CoordinatorRetrieveCacheResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta solo per la richiesta 'retrieve_cache'. Tale classe contiene:
 * ''error_domain''. Una stringa nullable.
 * ''error_code''. Una stringa nullable.
 * ''error_message''. Una stringa nullable. Queste sono tutte a ''null'' se l'esito è buono, mentre sono valorizzate se si vuole indicare una eccezione.
 * ''bookings''. Lista di liste di Booking. Essendo questa la risposta ad una richiesta di record per i livelli da ''lvl'' in su, l'elemento ''i''-esimo di questa lista va assegnato al membro ''bookings[i + lvl]'' della nostra memoria.
 * ''max_eldership''. Lista di int. L'elemento ''i''-esimo va assegnato al membro ''max_eldership[i + lvl]'' della nostra memoria.

----
Viene definita la classe interna !CoordinatorUnknownRequestResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta per le richieste che non sono state riconosciute dall'implementazione del server. Tale classe non contiene nulla.
 * ''pos''. Un intero. La posizione assegnata al livello ''lvl'' - 1, dove ''lvl'' è il valore contenuto nella relativa richiesta !CoordinatorReserveRequest.
 * ''eldership''. Un intero. L'anzianità del g-nodo appena costituito al livello ''lvl'' - 1.

----
Viene definita la classe interna !CoordinatorReplicaRecordRequest. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersRequest. Serve per la richiesta 'replica'. Tale classe contiene:
 * ''record''. Una istanza di !CoordinatorRecord.

----
Viene definita la classe interna !CoordinatorReplicaRecordSuccessResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta per la richiesta !CoordinatorReplicaRecordRequest. Significa esito positivo. Tale classe non ha membri.

----
Viene definita la classe interna !CoordinatorUnknownRequestResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta per le richieste che non sono state riconosciute dall'implementazione del server. Tale classe non ha membri.

Modulo Coordinator - Dettagli Tecnici

Requisiti

L'utilizzatore del modulo Coordinator inizializza il modulo richiamando il metodo statico init di CoordinatorManager. In tale metodo viene anche passata l'istanza di INtkdTasklet per fornire l'implementazione del sistema di tasklet.

Appena il nodo entra in una rete o ne costituisce una nuova, istanzia il suo CoordinatorManager passando al costruttore:

  • level_new_gnode. Il livello del g-nodo che ha costituito.

Quando il nodo ha completato la fase di bootstrap del modulo QSPN, il nodo informa il suo CoordinatorManager chiamando il metodo bootstrap_completed a cui passa:

  • peers_manager. L'istanza di PeersManager.

  • map. La mappa delle posizioni libere.

Prima di questo evento il nodo non è in grado di eseguire i metodi remoti chiamati da un suo vicino. Questa impossibilità viene segnalata con l'eccezione CoordinatorNodeNotReadyError.

Deliverables

Quando viene chiamato il suo metodo bootstrap_completed, il CoordinatorManager crea una istanza di CoordinatorService (che descriveremo sotto) e la registra nel PeersManager.


Il modulo permette di chiedere ad un vicino del nodo informazioni sui posti liberi nei suoi g-nodi con il metodo get_neighbor_map di CoordinatorManager.

Il metodo ha come argomento lo stub per contattare il vicino. Prevede le eccezioni CoordinatorStubNotWorkingError e CoordinatorNodeNotReadyError.

Restituisce una istanza di ICoordinatorNeighborMap.


Il modulo permette di chiedere ad un vicino del nodo di prenotare per lui un posto nel suo g-nodo di livello l con il metodo get_reservation di CoordinatorManager.

Il metodo ha come argomento lo stub per contattare il vicino e il livello a cui fare richiesta. Prevede le eccezioni CoordinatorStubNotWorkingError, CoordinatorNodeNotReadyError, CoordinatorInvalidLevelError e CoordinatorSaturatedGnodeError.

Restituisce una istanza di ICoordinatorReservation.

Comunicazioni tra vicini

Quando il modulo vuole chiedere ad un vicino informazioni sui posti liberi nei suoi g-nodi usa il metodo remoto retrieve_neighbor_map. Non ha argomenti. Prevede l'eccezione CoordinatorNodeNotReadyError, oltre alle solite StubError e DeserializeError. Restituisce una istanza di ICoordinatorNeighborMapMessage.

L'interfaccia ICoordinatorNeighborMapMessage è un segnaposto (vuota) esposto dalla libreria intermedia di ZCD che espone il metodo remoto. L'interfaccia ICoordinatorNeighborMap è esposta dal modulo Coordinator e ha i metodi descritti nell'analisi. L'implementazione del metodo remoto crea una istanza della classe NeighborMap. Questa è una classe serializzabile interna al modulo, che implementa entrambe le interfacce suddette.


Quando il modulo vuole chiedere ad un vicino di prenotare per lui un posto nel suo g-nodo di livello l usa il metodo remoto ask_reservation. Ha come argomento il livello l. Prevede le eccezioni CoordinatorNodeNotReadyError, CoordinatorInvalidLevelError e CoordinatorSaturatedGnodeError, oltre alle solite StubError e DeserializeError. Restituisce una istanza di ICoordinatorReservationMessage.

L'interfaccia ICoordinatorReservationMessage è un segnaposto (vuota) esposto dalla libreria intermedia di ZCD che espone il metodo remoto. L'interfaccia ICoordinatorReservation è esposta dal modulo Coordinator e ha i metodi descritti nell'analisi. L'implementazione del metodo remoto crea una istanza della classe Reservation. Questa è una classe serializzabile interna al modulo, che implementa entrambe le interfacce suddette.

Servizio Coordinator

Classe server del servizio Coordinator

Il modulo deriva da PeerService la classe CoordinatorService. All'interno di tale classe (per poter accedere ai suoi membri privati) definisce anche la classe DatabaseDescriptor che implementa l'interfaccia IFixedKeysDatabaseDescriptor.

La classe CoordinatorService è interna al modulo. Contiene questi membri:

  • levels. Intero. Il numero di livelli nella topologia della rete. Gli viene passato nel costruttore.

  • peers_manager. Riferimento al PeersManager. Gli viene passato nel costruttore.

  • fkdd. Contiene una istanza di DatabaseDescriptor valorizzata nel costruttore.

  • booking_lists[i], con i da 0 a levels - 1. Lista di Booking. Memoria delle prenotazioni. La lista di prenotazioni attive per il nostro g-nodo di livello i + 1. Il costruttore la crea vuota.

  • La classe Booking ha una posizione (int pos) e un time-to-live (Timer ttl). E' una classe serializzabile e interna al modulo.

  • max_elderships[i], con i da 0 a levels - 1. Intero. Il valore più alto di progressione già assegnato ad una prenotazione per un nuovo g-nodo di livello i dentro il nostro g-nodo di livello i + 1. Il costruttore lo inizializza a 0.

  • Quando assegnato ad un g-nodo, un valore di anzianità più alto significa che il g-nodo è arrivato dopo, cioè esso è più giovane.

Nel costruttore, dopo aver valorizzato i membri nel modo suddetto, viene fatta la registrazione sul PeersManager. Dopo, in una tasklet, si avvia il metodo fixed_keys_db_on_startup del PeersManager passando l'istanza fkdd e il livello del nuovo g-nodo costituito (che viene passato al costruttore). Questo per avvalersi degli algoritmi di gestione delle problematiche di mantenimento di un database a chiavi fisse.

Quando viene chiamato il metodo get_record_for_key dell'istanza fkdd, la classe CoordinatorService fa queste operazioni:

  • Assert: La chiave k passata è una istanza valida di CoordinatorKey.

  • Valorizza e restituisce una istanza di CoordinatorRecord con:

    • lvl: lo stesso livello che è nella chiave: k.lvl.

    • booking_list: una copia della lista nel membro booking_lists[lvl - 1] del CoordinatorService.

    • max_eldership: l'intero che è nel membro max_elderships[lvl - 1] del CoordinatorService.

Quando viene chiamato il metodo set_record_for_key dell'istanza fkdd, la classe CoordinatorService fa queste operazioni:

  • Assert: La chiave k passata è una istanza valida di CoordinatorKey.

  • Assert: Il record rec passato è una istanza valida di CoordinatorRecord.

  • booking_lists[rec.lvl - 1] = una copia della lista in rec.booking_list.

  • max_elderships[rec.lvl - 1] = rec.max_eldership.

Richieste fornite dal servizio

Ricordiamo che quando una richiesta ad un servizio peer-to-peer viene ricevuta dall'hash_node viene chiamato il metodo exec della classe che deriva la PeerService, nel nostro caso la classe CoordinatorService. Questo metodo riceve una istanza di IPeersRequest (e la tupla che identifica il chiamante, ma in questo caso non la utiliziamo) e deve restituire una istanza di IPeersResponse.

Il metodo exec della classe CoordinatorService, per avvalersi degli algoritmi di gestione delle problematiche di mantenimento di un database a chiavi fisse, chiama il metodo fixed_keys_db_on_request del PeersManager passando l'istanza fkdd. Questo metodo del modulo PeerServices restituisce il controllo al modulo Coordinator richiamando sull'istanza fkdd il metodo execute definito così dall'interfaccia IFixedKeysDatabaseDescriptor:

  • IPeersResponse execute(IPeersRequest r) throws PeersRefuseExecutionError, PeersRedoFromStartError

Metodo reserve

Se il metodo execute della classe CoordinatorService.DatabaseDescriptor viene chiamato con una istanza di CoordinatorReserveRequest (si veda sotto) allora abbiamo ricevuto una richiesta di "prenota un posto nel tuo g-nodo di livello lvl". Le operazioni da fare sono:

  • Se l'argomento lvl ha un valore non accettabile viene fatta pervernire una eccezione CoordinatorInvalidLevelError. Lo si fa valorizzando appositamente i campi error_* di una nuova istanza di CoordinatorReserveResponse che viene restituita.

  • Atomic on: queste operazioni devono essere eseguite atomicamente, senza permettere la schedulazione di altre tasklet.
  • Verifica nella mappa map del CoordinatorManager quali posizioni sono libere nel livello lvl. Se nessuna posizione risulta libera, lancia una eccezione CoordinatorSaturatedGnodeError (campi error_* di CoordinatorReserveResponse).

  • Dalla lista ottenuta di posizioni libere, esclude quelle presenti nelle prenotazioni booking_lists[lvl - 1] del CoordinatorService. Nello scorrere questa lista, rimuove le istanze di Booking che sono scadute (membro ttl). Se nessuna posizione risulta libera, lancia una eccezione CoordinatorSaturatedGnodeError (campi error_* di CoordinatorReserveResponse).

  • Scegli una posizione pos.

  • Segnala la prenotazione nella memoria. Cioè crea una istanza di Booking pos con ttl a 60 secondi (una private const nel codice) nella lista booking_lists[lvl - 1] del CoordinatorService.

  • Incrementa di 1 la max_elderships[lvl - 1] nella memoria.

  • Con i dati in memoria prepara la risposta ret, una istanza di CoordinatorReserveResponse con:

    • pos = pos.
    • eldership = max_elderships[lvl - 1].
  • Atomic off.
  • Avvia la prima replica con il metodo PeersManager.begin_replica. Questi sono gli argomenti:

    • Come q mettiamo 15 (una private const nel codice).

    • Come perfect_tuple il valore ottenuto con il metodo perfect_tuple della classe CoordinatorClient con la chiave lvl.

    • Costruisce la richiesta r di tipo 'replica_record' il cui contenuto si ottiene con il metodo get_record_for_key di fkdd.

  • Attende l'esito, cioè il booleano restituito, la risposta resp da interpretare, l'oggetto IPeersContinuation cont da passare nelle chiamate successive.

  • Il booleano restituito è False solo se si è avuta l'eccezione NoParticipantsInNetwork. In questo caso si ignora la risposta resp.

  • La risposta resp dovrebbe essere semplicemente OK, cioè una istanza di CoordinatorReplicaReserveResponse con i membri a null. Altrimenti va prodotto un warning descrittivo da usare nella fase di debug del modulo; quando il modulo è stabile questa situazione va semplicemente ignorata.

  • Se l'esito è True:

    • Avvia la seconda replica con il metodo PeersManager.next_replica passando la IPeersContinuation cont.

    • Attende l'esito, cioè il booleano restituito e la risposta resp da interpretare.

    • Il booleano restituito è False solo se si è avuta l'eccezione NoParticipantsInNetwork. In questo caso si ignora la risposta resp.

    • La risposta resp dovrebbe essere semplicemente OK. Altrimenti va prodotto un warning descrittivo.

    • Se l'esito è True:

      • Avvia una tasklet in cui:
        • While True:
          • Avvia una replica con il metodo PeersManager.next_replica passando la IPeersContinuation cont.

          • Attende l'esito, cioè il booleano restituito e la risposta resp da interpretare.

          • Il booleano restituito è False se abbiamo completato q repliche o se si è avuta l'eccezione NoParticipantsInNetwork. In questo caso si ignora la risposta resp.

          • La risposta resp dovrebbe essere semplicemente OK. Altrimenti va prodotto un warning descrittivo.

          • Se l'esito è False:

            • Esci dal ciclo. Termina la tasklet.
  • Restituisce ret.

Classe client del servizio Coordinator

Il modulo deriva da PeerClient la classe CoordinatorClient.

Ridefinisce il suo metodo perfect_tuple. La chiave che può essere usata per calcolare l'hash_node è un intero l da 1 a levels. Si può generare la tupla inizialmente con l'implementazione di base del metodo perfect_tuple (quindi occorre definire il metodo hash_from_key), ma poi la tupla restituita deve avere solo i primi l elementi.

Richieste fatte al servizio

Ricordiamo che questo servizio è non opzionale. Il nodo stesso che fa la richiesta è partecipante al servizio ed è sempre incluso nel g-nodo in cui la ricerca viene eventualmente circoscritta. Quindi l'eccezione PeersNoParticipantsInNetworkError rilanciata dal metodo call della classe base PeerClient può sempre essere trattata come un errore.

Ricordiamo inoltre che la gestione di un database a chiavi fisse è tale che un nodo può rifiutarsi di rispondere perché non esaustivo solo in operazioni di sola lettura. Nelle chiamate al metodo call della classe base PeerClient fatte per richieste che non sono di sola lettura (ad esempio la richiesta 'reserve') l'eccezione PeersDatabaseError può essere trattata come un errore.

Metodo reserve

Se il nodo corrente vuole prenotare un posto nel suo g-nodo di livello lvl fa al servizio Coordinator la richiesta CoordinatorReserveRequest con la chiave lvl. Per farlo chiama il metodo reserve nella classe CoordinatorClient.

Il metodo prende a parametro il livello lvl. Restituisce una istanza di Reservation. Può rilanciare le eccezioni: CoordinatorSaturatedGnodeError.

In caso di comportamento anomalo del nodo che risponde al servizio, il metodo della classe CoordinatorClient rilancia una eccezione CoordinatorSaturatedGnodeError. Non possiamo in ogni caso sapere se il nodo che risponde agisce in modo corretto; anche se sappiamo che non agisce correttamente non sappiamo se lo fa per errore o con malizia; non possiamo sapere se risponderà in modo palesemente errato anche alle richieste provenienti da altri nodi; in definitiva, cercare di porre rimedio diversamente sarebbe inutile.

Le operazioni da fare sono:

  • Verifica che il livello lvl sia nel range valido. Altrimenti il programma abortisce con un messaggio di errore.

  • Prepara la richiesta r, il tempo timeout_exec, la chiave k.

  • IPeersResponse resp.

  • Reservation ret.

  • Try:
    • Richiama il metodo call della classe base. resp = call(k, r, timeout_exec).

  • Se riceve PeersNoParticipantsInNetworkError:

    • Il programma abortisce con un messaggio di errore.
  • Se riceve PeersDatabaseError:

    • Il programma abortisce con un messaggio di errore.
  • Se resp è una istanza di CoordinatorReserveResponse:

    • Se resp.error_* non sono tutte a null:

      • Se i campi error_* indicano un CoordinatorSaturatedGnodeError:

        • Rilancia l'eccezione CoordinatorSaturatedGnodeError con il messaggio ricevuto. Termina l'algoritmo.

      • Scrive un warning riportando l'eccezione indicata e il messaggio ricevuto.
      • Rilancia l'eccezione CoordinatorSaturatedGnodeError riportando l'eccezione indicata e il messaggio ricevuto. Termina l'algoritmo.

    • Se resp.pos non è coerente con la topologia in lvl - 1, oppure è la mia stessa posizione in lvl - 1:

      • Scrive un warning.
      • Rilancia l'eccezione CoordinatorSaturatedGnodeError. Termina l'algoritmo.

    • Se resp.eldership < 1:

      • Scrive un warning.
      • Rilancia l'eccezione CoordinatorSaturatedGnodeError. Termina l'algoritmo.

    • Se resp.pos non è coerente con la topologia in lvl - 1:

      • Scrive un warning.
      • Rilancia l'eccezione CoordinatorSaturatedGnodeError. Termina l'algoritmo.

    • Prepara ret una istanza di Reservation con:

      • levels: map.get_levels().

      • gsizes: una lista di levels elementi: gsizes[i] = map.get_gsize(i).

      • lvl: lvl - 1.

      • pos: resp.pos.

      • eldership: resp.eldership.

      • upper_pos: una lista di levels - lvl - 1 elementi: upper_pos[i] = map.get_my_pos(i+lvl+1).

      • upper_elderships: una lista di levels - lvl - 1 elementi: upper_pos[i] = map.get_eldership(i+lvl+1).

  • Altrimenti:
    • # resp è una classe non prevista:

    • Scrive un warning.
    • Rilancia l'eccezione CoordinatorSaturatedGnodeError. Termina l'algoritmo.

  • Restituisce ret.

Classi serializzabili per le comunicazioni

Viene definita la classe interna CoordinatorKey. Si tratta di una classe serializzabile che deriva da Object. Serve per la rappresentare una chiave del servizio, cioè il livello del g-nodo da coordinare. Tale classe contiene:

  • lvl. Un intero da 1 a levels che indica il livello del g-nodo da coordinare.


Viene definita la classe interna CoordinatorRecord. Si tratta di una classe serializzabile che deriva da Object. Serve per la rappresentare un record del servizio, cioè la memoria condivisa del g-nodo da coordinare. Tale classe contiene:

  • lvl. Un intero da 1 a levels che indica il livello del g-nodo da coordinare.

  • booking_list. Lista di Booking. Memoria delle prenotazioni. La lista di prenotazioni attive per il nostro g-nodo di livello lvl.

  • max_eldership. Intero. Il valore più alto di progressione già assegnato ad una prenotazione per un nuovo g-nodo di livello lvl - 1 dentro il nostro g-nodo di livello lvl.


Viene definita la classe interna CoordinatorReserveRequest. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersRequest. Serve per la richiesta 'reserve'. Tale classe contiene:

  • lvl. Un intero che indica il livello del g-nodo dentro il quale riservare un posto.


Viene definita la classe interna CoordinatorReserveResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta per la richiesta CoordinatorReserveRequest. Tale classe contiene:

  • error_domain. Una stringa nullable.

  • error_code. Una stringa nullable.

  • error_message. Una stringa nullable. Queste sono tutte a null se l'esito è buono, mentre sono valorizzate se si vuole indicare una eccezione.

  • pos. Un intero. La posizione assegnata al livello lvl - 1, dove lvl è il valore contenuto nella relativa richiesta CoordinatorReserveRequest.

  • eldership. Un intero. L'anzianità del g-nodo appena costituito al livello lvl - 1.


Viene definita la classe interna CoordinatorReplicaRecordRequest. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersRequest. Serve per la richiesta 'replica'. Tale classe contiene:

  • record. Una istanza di CoordinatorRecord.


Viene definita la classe interna CoordinatorReplicaRecordSuccessResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta per la richiesta CoordinatorReplicaRecordRequest. Significa esito positivo. Tale classe non ha membri.


Viene definita la classe interna CoordinatorUnknownRequestResponse. Si tratta di una classe serializzabile che implementa l'interfaccia segnaposto (vuota) IPeersResponse. Serve come risposta per le richieste che non sono state riconosciute dall'implementazione del server. Tale classe non ha membri.

Netsukuku/ita/docs/ModuloCoordinator/DettagliTecnici (last edited 2015-12-02 09:12:54 by lukisi)