next up previous
Siguiente: 5. Conclusiones y trabajo Subir: Tesis Doctoral Anterior: 3. Componentes para la

Subsecciones

4. Componentes para extensión de RMI, JDBC y transferencia de estado

En el capítulo 3 se han propuesto nuevos componentes que facilitan la comunicación con grupos abiertos de procesos distribuidos y la detección de fallos de nodos. Su principal inconveniente es que para ser utilizados en aplicaciones Java ya existentes el programador debe conocer su API (Application Programming Interface) [2] e incorporar en sus programas la secuencia de llamadas correspondiente. Esto obviamente dificulta la adaptación de esta tecnología a aplicaciones actuales escritas con Java.

Para reducir el coste de incorporación de esta tecnología en aplicaciones Java ya existentes, en este capítulo se presentan dos componentes adicionales. El primer componente gestiona el acceso transparente mediante RMI (Remote Method Invocation) a grupos de objetos remotos. El segundo componente gestiona el acceso transparente mediante JDBC (Java Database Connectivity) a grupos de bases de datos distribuidas. Para desarrollar estos componentes se aplicó MARDARC [15]: una metodología para automatizar el desarrollo de aplicaciones robustas y cooperativas. MARDARC encapsula la aplicación original interconectando de manera automática o semiautomática los componentes necesarios.

Además, para facilitar la escritura de aplicaciones distribuidas Java con requisitos de alta disponibilidad [15,40,41,53,60,67] se propone un componente adicional que gestiona el paso de estado entre los miembros de un grupo de procesos. Este componente puede utilizarse para: 1) Añadir nuevos miembros a un grupo de réplicas para aumentar su grado de tolerancia a fallos, y 2) Reemplazar componentes distribuidos sin interrumpir el servicio de la aplicación.

La estructura de este capítulo es la siguiente: en los apartados 4.14.2 se introducen dos nuevos componentes que extienden las APIs de Java RMI y JDBC para que sean utilizadas de forma transparente con grupos de procesos distribuidos. Finalmente, en el apartado 4.3.1 se presenta un componente adicional que implementa un protocolo para transferencia cooperativa de estado en un grupo de replicas.

4.1 Nuevo componente para extender RMI con soporte para comunicación con grupos

El lenguaje de programación Java proporciona soporte para invocación remota de objetos RMI [2]. Sin embargo, la API RMI de Java no proporciona soporte para la invocación remota de un grupo de objetos distribuidos. Para eliminar esta carencia, en este apartado se propone una extensión de esta API que utiliza los componentes 1-a-N y N-a-1 descritos en el capítulo 3 como soporte para comunicación fiable con grupos abiertos de objetos remotos.

Como punto de partida consideremos un ejemplo sencillo con un único cliente y un servidor: el servidor contiene varios objetos a los que pueden acceder los clientes mediante la invocación remota de métodos pertenecientes a objetos localizados en el servidor. Para ofrecer este servicio, el servidor crea una instancia de cada objeto y registra sus identificadores para que sean utilizados por el cliente como enlace con los objetos remotos. Además, es necesario generar el stub (en el lado cliente) y el skeleton (en el lado servidor) de cada objeto remoto.

Para conseguir la nueva versión con grupos abiertos de objetos remotos, en el lado del cliente se remplaza la implementación de la API RMI (conservando su interfaz de usuario) por un suplente (stub-c) que redirige las llamadas (de forma transparente al usuario) al grupo de servidores remotos utilizando el componente de comunicación 1-a-N descrito en el capítulo 3. En el lado del servidor, otro suplente (stub-s) recibe las invocaciones remotas, y las reencamina hacia el objeto correspondiente realizando una invocación local al objeto utilizando Java RMI. Finalmente, stub-s empaqueta y envía hacia stub-c el resultado de la invocación remota para que filtre todas las respuestas.

El uso de este nuevo componente es prácticamente transparente a las aplicaciones escritas mediante Java RMI: solamente requiere recompilar la aplicación Java sustituyendo la API estándar (java.rmi) por la nueva API (jgroups.rmi).

En caso de que el código del objeto sea determinista, este componente puede utilizarse para implementar una versión robusta del objeto remoto mediante la técnica de replicación activa [9,42]: el componente de comunicación 1-a-N asegura que todas las réplicas reciben exactamente la misma secuencia de invocaciones remotas, y si todas las réplicas del objeto remoto realizan exactamente la misma secuencia de acciones, generan exactamente el mismo resultado. Al recibir resultados idénticos, el cliente puede continuar su ejecución desde que recibe la primera respuesta.

Figura 4.1: Extensión de la API RMI
\includegraphics[scale=0.6]{figures/middleware_rmi.eps}

Los aspectos más relevantes de la implementación de este componente middleware son (ver Figura 4.1):

4.2 Nuevo componente para extender JDBC con soporte para comunicación 1-a-N

JDBC (Java Database Connectivity) [2] es una API Java que proporciona conectividad y acceso remoto a bases de datos relacionales [10,11,29,61]. JDBC generaliza las funciones de acceso a las bases de datos más comunes, abstrayendo los detalles específicos del fabricante de una determinada base de datos.

De forma similar a RMI, JDBC no da soporte de llamada a un grupo de bases de datos distribuidas. Para eliminar esta carencia, en este apartado se propone una extensión de JDBC que gestiona el acceso transparente a un grupo de servidores de base de datos relacionales. Para llevar a cabo esta extensión utilizaremos el componente para RMI 1-a-N descrito en el apartado anterior, es decir, empleamos jgroups.rmi como soporte de comunicación.

De forma similar al componente de extensión de RMI descrito en el apartado anterior, para desarrollar este nuevo componente se remplaza, en el lado del cliente, la implementación de la API JDBC por un suplente (stub) que utiliza jgroups.rmi para redirigir las invocaciones a objetos remotos hacia un grupo de servidores remotos. En cada uno de los miembros del grupo servidor se añade un suplente que recibe las invocaciones remotas y las encamina hacia su copia local de la API JDBC. Si la gestión de la base de datos es determinista, esta técnica permite implementar bases de datos replicadas mediante replicación activa [9,42]: al tratarse de una aplicación determinista, todas las réplicas de la base de datos realizan la misma secuencia de acciones y generan el mismo resultado. Para utilizar esta versión robusta de la base de datos relacional solamente es necesario sustituir en el código del cliente el uso de la API estándar (java.sql) por la nueva API (jgroups.sql).

Aunque la filosofía general de desarrollo del componente es sencilla, su correcta implementación requiere de detalles propios de la semántica de JDBC. Por ejemplo, JDBC permite obtener una o más conexiones para una o más bases de datos utilizando uno o varios drivers JDBC diferentes, y cada driver debe ser registrado antes de poder utilizarse. Para registrar un driver de base de datos es imprescindible identificarlo mediante su URL, que consta de la siguiente información: ubicación física y nombre identificativo. Al replicar la base de datos ha sido necesario reemplazar la dirección física del driver por una dirección lógica que represente el grupo de servidores de bases de datos. En jgroups.sql, el URL representa: el canal de JGroups empleado en la comunicación entre procesos y el nombre del servicio.

Cuando el cliente llama al método que comprueba si el driver de base de datos está presente, se realiza la inicialización de su middleware. Con esta llamada, el cliente conecta con el servidor (que también habrá inicializado su middleware) para buscar el objeto remoto que implementa este método y retorna su referencia (en caso de encontrarse). Esta referencia al objeto, que en realidad es su suplente, permite que la llamada al método de la API jgroups.jdbc se convierta en una invocación remota (en cada servidor) del mismo método pero, ahora, de la API java.jdbc.

Al utilizar jgroups.rmi como soporte de comunicación entre los suplentes de la API jgroups.jdbc, el servidor debe asociar el objeto antes de recibir invocaciones desde el cliente (ver Figura 4.2). Para ello el servidor registra las clases proporcionadas por jgroups.sql (almacenadas como objetos accesibles remotamente). El cliente accede a los objetos asociados a estas clases a partir de sus suplentes (obtenidos desde el servidor). Finalmente, las clases de jgroups.sql del servidor acceden a sus correspondientes para java.sql.

Figura 4.2: El componente RMI como soporte de comunicación para el componente JDBC
\includegraphics[scale=0.55]{figures/middleware_jdbc.eps}

La llamada al método para obtener una conexión retorna al cliente (desde el servidor) la referencia a un objeto que permite efectuar operaciones sobre la conexión con la base de datos. Para conservar activa esta conexión, el servidor mantiene abierta una conexión con la base de datos local mientras el cliente lo haga con él. Para lograrlo, el objeto de la API java.sql es instanciado y manejado internamente por la API jgroups.sql. Al utilizar jgroups.rmi, el servidor asocia el objeto antes de poder retornar al cliente una referencia a este objeto cuando se lo solicite.

Para gestionar varias conexiones, el suplente del servidor dispone de un registro donde almacena cada una de estas conexiones. De esta forma el servidor sólo necesita saber que conexión desea usar el cliente para que todo funcione correctamente. Para ello, el servidor asigna un identificador único a cada conexión activada desde el cliente. Este identificador se pasa al cliente en el momento de retornar el suplente del objeto para la conexión. Esta operación es transparente al usuario.

Antes de poder ejecutar una sentencia SQL sobre la base de datos, el cliente debe obtener el objeto que se lo permita. Una vez dispone de él, puede utilizarlo para ejecutar cualquier operación contra la base de datos. Para obtener una referencia al objeto, el cliente utiliza la referencia a la conexión retornada desde el servidor. La llamada al método para ejecutar una sentencia SQL utiliza el identificador de la conexión activa para acceder al objeto conexión correspondiente en el lado servidor, instanciado y manejado internamente por la API jgroups.sql. Allí, el servidor invoca este método contra la base de datos para obtener el objeto buscado, que es instanciado y manejado internamente por la API jgroups.sql, y devuelto al cliente como una referencia cuando la solicite.

Puesto que un cliente puede ejecutar varias sentencias con la misma base de datos de forma simultánea, el servidor dispone de un registro donde almacena cada una de estas instancias.

Finalizamos aquí esta breve descripción de cómo se han extendido dos componentes estándar de Java para que incorporen comunicación con grupos de procesos. De forma similar, utilizando MADARC (la metodología descrita en [15]) y los componentes descritos en este documento, es viable extender otras APIs estándar de Java para conseguir su versión robusta o cooperativa. Para completar este escenario de componentes reutilizables, en el siguiente apartado se describe un componente adicional que proporciona soporte para transferencia de estado entre los miembros de un grupo de réplicas.


4.3 Nuevo componente para transferencia cooperativa de estado

Determinadas aplicaciones requieren reiniciar servidores que hayan fallado. En estos casos, las réplicas existentes deben transferir el estado a la nueva réplica de una manera consistente antes que pueda iniciar su actividad normal. Para que el servicio no se vea interrumpido, la transferencia del estado debe efectuarse mientras el resto de las réplicas ya existentes continúan atendiendo las solicitudes de servicio recibidas desde los clientes.

En este apartado se presenta un nuevo componente para transferencia cooperativa de estado entre los miembros de un grupo de réplicas implementando el protocolo descrito en [15]. En primer lugar se presenta brevemente el protocolo de transferencia de estado, y a continuación se exponen algunos detalles de su implementación.


4.3.1 Descripción del protocolo

Para entender la problemática asociada con la transferencia de estado [1,45], consideremos un grupo replicado compuesto de dos miembros (S1 y S2) que procesan las peticiones llegadas desde otros dos clientes (C1 y C2), y analicemos la transferencia de estado que se realiza en el grupo replicado ante la incorporación de una nueva réplica S3. Siguiendo la propuesta descrita en [15], las fases del protocolo de transferencia de estado son (ver Figura 4.3):

a)
El nuevo miembro (S3) envía su petición de entrada en el grupo servidor. El subsistema de comunicación con grupos notifica a todas las réplicas del grupo (incluyendo S3) que hay una nueva vista en la que se ha incorporado S3.

b)
Ante este hecho, S1, S2, y S3 pasan a un estado en el que dejan de atender peticiones y comienzan a encolar todas las peticiones que reciban de los clientes. Además, S1 y S2 vuelcan su estado en un fichero.

c)
Tras completar el volcado en el fichero, S1 y S2 vuelven al estado normal de procesamiento de peticiones, comenzando a atender las peticiones que han sido encoladas mientras realizaron el volcado del estado en el fichero (de esta forma, el retardo ocasionado por la transferencia de estado tiene un impacto mínimo en la continuidad del servicio). Simultáneamente, S1 y S2 comienzan a transferir el estado almacenado en el fichero hacia S3; hasta que no finalice la transferencia completa del fichero, S3 continúa encolando las peticiones que están siendo procesadas por S1 y S2.

d)
Una vez finalizada la transferencia del fichero, S3 reconstruye el estado y comienza a procesar todas las peticiones que tiene encoladas. Tras esta fase eventualmente el nuevo miembro estará integrado en el grupo servidor y continuará su comportamiento como un miembro más del grupo.

Figura 4.3: Transferencia de estado
\includegraphics[scale=0.45]{figures/fases_transferencia_estado.eps}

Para evitar sobrecarga de la red durante la transferencia de estado (ya que al ser réplicas, S1 y S2 estarían enviando a S3 copias exactas del mismo fichero) pueden implementarse varias técnicas [15,45,1]. Por ejemplo, podemos adoptar como solución que sólo uno de los miembros del grupo servidor (S1 o S2) sea quién envíe el estado. La elección del miembro que realice la transferencia del estado puede quedar en manos del grupo o del nuevo proceso. La opción propuesta en [15] es elegir al coordinador del grupo. Si este proceso falla durante la transferencia del estado, el nuevo coordinador finaliza la transferencia. Una alternativa mejor (ya que balancea la carga entre todos los servidores) es realizar una transferencia cooperativa del estado. Para ello se fragmentar el estado y se asigna a S1 y S2 la transferencia de un fragmento hacia S3. Si durante la transferencia cooperativa, alguno de los miembros del grupo falla, su fragmento se reparte de nuevo entre el resto de los miembros que están transfiriendo el estado para su inmediata transferencia.

A nivel de implementación, la aplicación de esta técnica requiere el uso de un canal separado para realizar la transferencia de estado; en caso de utilizar el mismo canal para la transferencia de estado y el envío de peticiones y respuestas, es bastante probable que el número de mensajes que un proceso deba encolar sea mayor que la capacidad de la cola, lo que obligaría a desechar mensajes y complicaría innecesariamente la implementación. Este problema desaparece llevando a cabo la transferencia del estado por un canal diferente.

Aplicando esta configuración, consideremos ahora un sistema donde existe un grupo replicado (Main) compuesto de dos miembros (S1 y S2) que procesan las peticiones llegadas desde otros dos clientes (C1 y C2). Este grupo Main representa el canal de comunicación cliente-servidor. Para implementar el canal para transferencia de estado se crea un nuevo grupo llamado Transfer (con idéntica membresía que el grupo Main para proporcionar tolerancia a fallo). De esta forma, todos los servidores son miembros de dos grupos simultáneamente (grupos solapados): Main para procesar las peticiones/respuestas llegadas desde los clientes/servidores y Transfer para realizar la transferencia de estado a una nueva réplica que se una al grupo servidor (ver Figura 4.4). Los servicios de comunicación actuales disponen de mecanismos para diferenciar entre mensajes llegados a procesos que pertenecen a más de un grupo.

Figura 4.4: Canal independiente para transferencia de estado
\includegraphics[scale=0.55]{figures/dos_canales.eps}

El comportamiento del nuevo sistema, con la incorporación de este grupo solapado para transferencia de estado, es el que se describe a continuación (ver Figura 4.5):

a)
Un nuevo miembro (S3) se une al grupo Main. En el momento de hacerlo, todos los servidores del grupo detectan este cambio mediante la llegada de una nueva vista donde S3 ya está incluido.

b)
Ante este hecho, todos los procesos que formaban el grupo (S1 y S2), dejan de atender las peticiones de los clientes (aunque son encoladas) y vuelcan su estado a un fichero.

c)
Una vez finalizada esta operación de volcado, estos servidores (S1 y S2) pueden seguir atendiendo las peticiones de los clientes. Las primeras en ser atendidas son aquellas que fueron encoladas durante el proceso de volcado y, luego, las que vayan llegando posteriormente. Al mismo tiempo, S3 se une el grupo Transfer y comienza a encolar las peticiones llegadas al grupo Main desde los clientes, aunque no podrá atenderlas hasta que la transferencia del estado haya finalizado. Después del volcado del estado, el grupo Transfer (S1 y S2) está en disposición de iniciar la transferencia del estado hacia el nuevo miembro (S3).

d)
Una vez finalizada la transferencia, S3 reconstruye el estado a partir de la información recibida. Reconstruido el estado, S3 comienza a atender las peticiones de los clientes. Las primeras en ser atendidas son aquellas que fueron encoladas durante la transferencia (pueden haber modificado el estado) y, luego, las que vayan llegando posteriormente. En este punto, el nuevo miembro (S3) puede considerarse integrado completamente en el grupo Main y desarrollar su actividad normal. S3 seguirá perteneciendo al grupo Transfer para posteriores transferencias de estado a nuevas réplicas que se unan al grupo Main.

Figura 4.5: Transferencia de estado con canal independiente
\includegraphics[scale=0.45]{figures/fases_dos_canales.eps}

Esta solución tiene algunos problemas y para analizarlos consideremos ahora un nuevo escenario. Dos clientes C1 y C2 envían peticiones a un grupo Main compuesto de un sólo miembro S1. Dos procesos, S2 y S3, desean unirse al grupo servidor (al mismo tiempo) para aumentar el grado de tolerancia a fallos.

Supongamos que la primera petición de unión en atenderse es la de S2. Entonces, S1 vuelca el estado a un fichero para su posterior transferencia. Ahora, S1 comienza la transferencia del estado hacia S2 utilizando el grupo auxiliar Transfer. Mientras tiene lugar esta operación, S3 se une al grupo Main (ahora compuesto por S1 y S2). S1 transfiere el estado a S3 utilizando el grupo Transfer pero S2 no puede hacerlo hasta que no finalice la operación de transferencia y reconstruya el estado.

En este punto, S2 y S3 están recuperando el estado de forma simultánea. Si S3 es más rápido que S2 en esta operación y S1 falla cuando S3 ha recuperado completamente el estado pero S2 no ha terminado todavía, el grupo Main queda formado por dos procesos con estados diferentes (S3 tiene el estado completo pero S2 lo tiene incompleto). Como puede deducirse del resultado, se ha creado una inconsistencia entre las réplicas del grupo servidor.

Esta inconsistencia en el estado puede evitarse si, despues de un fallo, se fuerza a que cualquier servidor que no haya recuperado el estado en su totalidad, abandone el grupo y vuelva a unirse nuevamente. Sin embargo, esta no es una opción aceptable cuando se desea preservar la continuidad en el servicio ofrecido por el grupo servidor.

La problemática de este nuevo escenario surge cuando varios procesos recuperan el estado al mismo tiempo. Una aproximación mejor consiste en serializar la entrada de nuevos miembros. Así, sólo un proceso recupera el estado en cualquier instante. Para restringir la entrada de nuevas réplicas se añade un nuevo grupo llamado Sequencer formado por todos aquellos procesos que esperan su turno para unirse al grupo servidor.

La función de este nuevo grupo solapado es la de secuenciar la entrada de nuevos miembros (ver Figura 4.6). Para realizar este secuenciamiento sólo se permite que el coordinador del grupo Sequencer (formado por S2 y S3) pueda unirse al grupo Main y recibir el estado desde todos los procesos ya existentes usando el grupo Transfer (S2, en este caso). El siguiente proceso en unirse al grupo (S3) debe esperar hasta convertirse en nuevo coordinador del grupo Sequencer. Esto ocurre cuando el proceso que actualmente recupera el estado, lo reciba en su totalidad y abandone el grupo Sequencer.

Figura 4.6: Secuenciamiento en la incorporación de nuevas réplicas
\includegraphics[scale=0.55]{figures/grp_secuenciador.eps}

La funcionalidad de la nueva configuración, con este nuevo grupo solapado para secuenciamiento en la entrada de nuevas réplicas, es el que se describe a continuación (ver Figura 4.7):

a)
Dos nuevos miembros (S2 y S3) van a unirse al grupo Main. Antes de hacerlo, se unen primero (de forma temporal) al grupo secuenciador Sequencer. El primero que lo consiga se convierte en su coordinador (S2) y, por tanto, puede unirse al grupo Main. En ese momento, todos los servidores del grupo detectan este cambio mediante la llegada de una nueva vista donde S2 ya está incluido.

b)
Ante este hecho, todos los procesos que formaban el grupo antes de la llegada de la nueva vista (S1), dejan de atender las peticiones de los clientes (aunque son encoladas) y vuelcan su estado a un fichero.

c)
Una vez finalizada esta operación de volcado, estos servidores (S1) pueden seguir atendiendo las peticiones de los clientes. Las primeras en ser atendidas son aquellas que fueron encoladas durante el proceso de volcado y, luego, las que lleguen posteriormente. Al mismo tiempo, S2 se une al grupo Transfer y comienza a encolar las peticiones llegadas al grupo Main desde los clientes, aunque no podrá atenderlas hasta que la transferencia del estado haya finalizado. Después del volcado del estado, el grupo Transfer está en disposición de comenzar la transferencia del estado hacia el nuevo miembro (S2).

d)
Una vez finalizada la transferencia, S2 reconstruye el estado a partir de la información recibida. Reconstruido el estado, S2 comienza a atender las peticiones de los clientes. Las primeras en ser atendidas son aquellas que fueron encoladas durante la transferencia (pueden haber modificado el estado) y, luego, las que lleguen posteriormente. En este punto, el nuevo miembro (S2) puede considerarse integrado completamente en el grupo Main y desarrollar su actividad normal. S2 seguirá perteneciendo al grupo Transfer para posteriores transferencias de estado pero abandona el grupo Sequencer para que otro proceso en espera (el nuevo coordinador) pueda unirse al grupo Main. En el momento que S2 abandona el grupo Sequencer y S3 se convierta en coordinador, se repiten de nuevo todos los pasos descritos para S2.

Figura 4.7: Transferencia de estado con secuenciamiento de nuevas réplicas
\includegraphics[scale=0.45]{figures/fases_grp_secuenciador.eps}

4.3.2 Componente para transferencia cooperativa de estado

En este apartado se presenta un nuevo componente que implementa el protocolo descrito en el apartado anterior y utiliza tres canales independientes: un canal secundario utilizado para el volcado del estado (Transfer), un canal principal (Main) para atender las peticiones llegadas desde los clientes, y un tercer canal que se utiliza para ordenar la incorporación simultánea de varias réplicas (Sequencer). Esta configuración de canales queda representada en la Figura 4.8.

Figura 4.8: Configuración de canales del protocolo de transferencia cooperativa de estado
\includegraphics[scale=0.4]{figures/cfg_canales_estado_cooper.eps}

Los procesos que se conectan al canal Main pueden hacerlo con funcionalidad de cliente o servidor. Sin embargo, los que se conecten al canal Transfer deben hacerlo como servidores activos (AS) o servidores inactivos (IS). Se entiende por servidor activo aquel que ya atiende peticiones llegadas desde los clientes en el canal principal y por servidor inactivo aquel que aún no atiende peticiones (al no haber recibido aún el estado). Usando esta configuración de canales, en el momento que una nueva réplica (IS) se conecte al canal Transfer, las réplicas presentes (AS) cooperan en la transferencia del estado.

Así, cuando los servidores activos detecten un nuevo miembro en el grupo: 1) vuelcan el estado a un fichero y 2) envían al nuevo miembro (IS) la parte del fichero que le corresponda. La parte a enviar por cada réplica se determina atendiendo a su posición en la vista y al número de réplicas existente (membresía del grupo).

Por supuesto, la operación para transferir el estado debe contemplar el fallo de alguno de los procesos implicados. Veamos cuales son los diferentes escenarios que pueden presentarse:

Hemos explicado los problemas asociados con la transferencia cooperativa del estado. A continuación se analiza por separado cada uno de estos casos de estudio y se presentan soluciones, describiendo con detalle como se han implementado.

4.3.2.1 Caso I

Cuando un nuevo proceso se une al canal Transfer, lo hace como servidor inactivo (IS). Esta acción provoca un cambio de vista en todos los procesos conectados al canal y permite que los servidores activos (AS) determinen si el cambio de vista se ha producido porque un nuevo proceso ha entrado al canal, o bien, porque un proceso ya existente salió de él (o falló). Un cambio de vista debido a la entrada de una nueva réplica (IS) provoca que el resto de réplicas (AS) inicien la transferencia del estado.

En caso de incorporación de una nueva réplica (IS), los pasos seguidos por las réplicas ya existentes (AS) son: 1) detener el procesado de peticiones, 2) volcar el estado a un fichero, 3) procesar las peticiones encoladas durante el volcado a fichero y 4) seguir procesando las nuevas peticiones recibidas. Lógicamente, cada uno de los AS, mientras atiende nuevas peticiones recibidas por el canal Main, inicia la transferencia del estado hacia el IS usando el canal Transfer (ver Figura 4.9).

El IS, antes de empezar con la recepción del estado, prepara el correspondiente esquema de recuperación basándose en la vista del grupo. Este esquema de recuperación le permite determinar que fragmento del estado le va a transferir cada AS. Esta información es necesaria para reconstruir el fichero que contiene el estado. Por este motivo, antes de iniciar la transferencia del estado, los AS deben notificar (mediante un mensaje) al IS el tamaño del fichero (ver Figura 4.9-a). Entonces cada AS, atendiendo a la posición que ocupa dentro de la vista, al número de servidores activos disponibles y al tamaño del fichero a transferir, es capaz de determinar que fragmento de fichero ha de enviar al IS (ver Figura 4.9-b). De esta manera conseguimos que el IS comience a recibir el estado sin necesidad de requerírselo al grupo.

Figura 4.9: Diagrama de eventos del caso I
\includegraphics[scale=0.45]{figures/diag_transf_cooper_c1.eps}

Una vez recibido completamente el fichero y reconstruido el estado, se cambia el rol del proceso de IS a AS (ver Figura 4.9-c). A partir de este momento la nueva réplica puede considerarse totalmente integrada dentro del grupo servidor y puede ya desarrollar su actividad normal, es decir: 1) procesar las peticiones encoladas durante la operación de transferencia y 2) procesar las peticiones que sigan recibiéndose a continuación.

4.3.2.2 Caso II

Cuando un nuevo IS se une al canal Transfer, los AS inician de forma automática la transferencia del estado. El caso II se presenta cuando algún AS abandona el canal, por fallo, sin haber finalizado la transferencia de su fragmento del estado (ver Figura 4.10).

Figura 4.10: Diagrama de eventos del caso II
\includegraphics[scale=0.45]{figures/diag_transf_cooper_c2.eps}

Como ya sabemos, el IS dispone de un esquema de recuperación dónde registra la evolución de la transferencia del estado. Si en un momento dado, alguno de los AS (de los que el IS espera recibir un fragmento del estado) falla, la notificación del cambio de vista permite que el IS determine si el AS fallido terminó con éxito la transferencia de su fragmento o no (ver Figura 4.10-a). Para saberlo, revisa el esquema de recuperación utilizado. Si la transferencia no se hubiera completado, se crea un esquema sospechoso con el fragmento que falta por recibir.

Una vez finalizada la transferencia del estado, el IS comprueba si existe algún esquema sospechoso, y si fuera así, genera el correspondiente esquema de recuperación. Evidentemente, en este caso, la transferencia no ha finalizado completamente al no haberse recibido todos los fragmentos del fichero necesarios para reconstruir el estado.

Para recuperarlo, se envía un mensaje a todos los AS aún activos conteniendo, no sólo los límites del fragmento a recibir, sino también el número de AS de los cuales se espera obtener el estado (ver Figura 4.10-b). Cuando los AS reciben este mensaje, localizan los límites del fragmento y determinan el tamaño del bloque que han de transferir. A continuación, atendiendo a su posición en la vista y al número de AS presentes, dividen el fragmento del estado para determinar el bloque a transferir, de la misma forma que inicialmente se dividieron el estado completo (ver Figura 4.10-c). Acto seguido, da comienzo la transferencia del bloque en cuestión.

Esta operación se repite hasta que no existan esquemas sospechosos, momento en el cual la transferencia de estado finaliza con éxito. Lógicamente, el fallo de todos los AS impediría recuperar el estado.

Para finalizar, analicemos el caso en que uno de los AS falla después que el IS haya solicitado la recuperación del fragmento no transferido anteriormente debido al fallo de otro AS (ver Figura 4.11). Este fallo origina un nuevo cambio de vista en el grupo servidor. Sin embargo, si se recibe la nueva vista despues de que los AS hayan determinado su parte del fragmento, puede ocurrir que estos transfieran, atendiendo a su posición en la nueva vista, una parte del estado que no corresponde con el esperado por el IS para ese AS en concreto. Lógicamente esto provoca una inconsistencia en el fichero recibido en el IS e imposibilita la recuperación del estado.

Figura 4.11: Diagrama de eventos del caso III
\includegraphics[scale=0.58]{figures/diag_transf_cooper_c3.eps}

Esta problemática aparece porque el protocolo para transferencia cooperativa de estado ha sido diseñado para reducir el número de mensajes necesarios en la operación aprovechando la vista que mantiene cada uno de los procesos conectados al canal. Para evitarla, el IS debe notificar a todos los AS, además de los límites del bloque a transferir, el número de procesos AS existentes en el momento de preparar el esquema de recuperación (ver Figura 4.11-a). Cuando los AS reciben esta notificación, comparan el número de AS presentes con el notificado en el mensaje. En caso de no coincidir, no envían el fragmento y en su lugar se lo notifican al IS para que se considere nuevamente como no transferido (ver Figura 4.11-b). Como consecuencia, el IS crea un nuevo esquema de recuperación y solicita nuevamente a los AS disponibles el envío del fragmento pero, ahora, con el número de AS actualizado (ver Figura 4.11-c). Una vez transferido el fragmento restante (ver Figura 4.11-d) y recuperado el estado en su totalidad, el IS se convierte en AS y puede iniciar su actividad normal.


next up previous
Siguiente: 5. Conclusiones y trabajo Subir: Tesis Doctoral Anterior: 3. Componentes para la
Luis Hernandez