Escribir un cargador con un controlador de precarga de réplica

Un cargador con un controlador de precarga de réplica es un cargador que implementa la interfaz ReplicaPreloadController además de la interfaz del cargador.

La interfaz ReplicaPreloadController se ha diseñado para proporcionar un modo de que la réplica que se convierte en fragmento primario sepa si el fragmento primario anterior ha completado el proceso de precarga. Si la precarga se ha completado parcialmente, se ofrece información sobre el punto en el que se quedó la correlación primaria anterior. Con la implementación de la interfaz ReplicaPreloadController, una réplica que pasa a ser la primaria continúa el proceso de precarga en el punto donde se detuvo el primario anterior y continúa hasta finalizar la operación de precarga general.

En un entorno distribuido de WebSphere eXtreme Scale, una correlación puede tener réplicas y podría precargar un gran volumen de datos durante la inicialización. La precarga es una actividad del cargador y sólo tiene lugar en la correlación primaria durante la inicialización. La operación de precarga tardará en completarse si el volumen de datos que se va a precargar es muy grande. Si la correlación primaria ha precargado una parte considerable de datos, pero se ha detenido durante la inicialización sin motivo aparente, una réplica pasa a ser la réplica primaria. En esta situación, los datos que ha precargado la correlación primaria anterior se pierden porque la nueva réplica primaria normalmente realiza una precarga incondicional. Esto quiere decir que la nueva réplica primaria inicia el proceso de precarga desde el principio y pasa por alto los datos precargados previamente. Si desea que el nuevo primario empiece por el punto en que se detuvo el primario anterior durante el proceso de precarga, proporcione un cargador que implemente la interfaz ReplicaPreloadController. Si desea más información, consulte la documentación de la API.

Para obtener información sobre los cargadores, consulte Cargadores. Si está interesado en escribir un plug-in Loader regular, consulte Escribir un cargador.

La interfaz ReplicaPreloadController tiene la siguiente definición:
public interface ReplicaPreloadController
{
    public static final class Status
    {
        static public final Status PRELOADED_ALREADY = new Status(K_PRELOADED_ALREADY);
        static public final Status FULL_PRELOAD_NEEDED = new Status(K_FULL_PRELOAD_NEEDED);
        static public final Status PARTIAL_PRELOAD_NEEDED = new Status(K_PARTIAL_PRELOAD_NEEDED);
    }

    Status checkPreloadStatus(Session session, BackingMap bmap);
}

Las siguientes secciones describen algunos de los métodos de la interfaz Loader y ReplicaPreloadController.

Método checkPreloadStatus

Cuando un cargador implementa la interfaz ReplicaPreloadController, se llama al método checkPreloadStatus antes que el método preloadMap durante la inicialización de la correlación. El estado devuelto de este método determina si se llama al método preloadMap. Si este método devuelve Status#PRELOADED_ALREADY, se llama al método de precarga. De lo contrario, se ejecuta el método preload. Debido a este comportamiento, este método debe servir como método de inicialización del cargador. Debe inicializar las propiedades del cargador en este método. Este método debe devolver el estado correcto, o la operación de precarga podría no funcionar como se espera.

public Status checkPreloadStatus(Session session,
							BackingMap backingMap) {
        // Cuando un cargador implementa la interfaz ReplicaPreloadController,
			 // se llamará a este método antes que al método preloadMap durante la
			 // inicialización de la correlación. En función del estado devuelto de
			 //	este método, se llamará o no al método preloadMap. Por ello, este
    // método sirve también como el método de inicialización del cargador.
Este método
// debe devolver el estado correcto, si no, puede que la precarga no
			 // funcione como se espera.

        // Nota: debe inicializar esta instancia de cargador a continuación.
        ivOptimisticCallback = backingMap.getOptimisticCallback();
        ivBackingMapName = backingMap.getName();
        ivPartitionId = backingMap.getPartitionId();
        ivPartitionManager = backingMap.getPartitionManager();
        ivTransformer = backingMap.getObjectTransformer();
        preloadStatusKey = ivBackingMapName + "_" + ivPartitionId;

        try {
            // obtener preloadStatusMap para recuperar el estado de precarga
					 // que otras JVM podrían haber establecido.
            ObjectMap preloadStatusMap = session.getMap(ivPreloadStatusMapName);

            // recuperar el índice de fragmento de datos registrados por última vez.
            Integer lastPreloadedDataChunk = (Integer) preloadStatusMap.get(preloadStatusKey);

            if (lastPreloadedDataChunk == null) {
                preloadStatus = Status.FULL_PRELOAD_NEEDED;
            } else {
                preloadedLastDataChunkIndex = lastPreloadedDataChunk.intValue();
                if (preloadedLastDataChunkIndex == preloadCompleteMark) {
                    preloadStatus = Status.PRELOADED_ALREADY;
                } else {
                    preloadStatus = Status.PARTIAL_PRELOAD_NEEDED;
                }
            }

            System.out.println("TupleHeapCacheWithReplicaPreloadControllerLoader.
								checkPreloadStatus() 
					-> map = " + ivBackingMapName + ", preloadStatusKey = " + preloadStatusKey
                    + ", retrieved lastPreloadedDataChunk =" + lastPreloadedDataChunk + ",
                    determined preloadStatus = "
                    + getStatusString(preloadStatus));

        } catch (Throwable t) {
            t.printStackTrace();
        }

        return preloadStatus;
    }

Método preloadMap

La ejecución del método preloadMap depende del resultado devuelto del método checkPreloadStatus. Si se llama al método preloadMap, normalmente debe recuperar la información del estado de precarga en la correlación de estado de precarga designada y determinar cómo continuar. Lo ideal es que el método preloadMap sepa si la precarga se ha completado parcialmente y en qué punto debe comenzar exactamente. Durante la precarga de datos, el método preloadMap debe actualizar el estado de precarga en la correlación designada del estado de precarga. El estado de precarga que se almacena en la correlación de estado de precarga es recuperado por el método checkPreloadStatus cuando necesita comprobar el estado de la precarga.

public void preloadMap(Session session, BackingMap backingMap) 
throws LoaderException {
        EntityMetadata emd = backingMap.getEntityMetadata();
        if (emd != null && tupleHeapPreloadData != null) {
            // El método getPreLoadData es similar a captar datos
					 //  de una base de datos. Estos datos se pasarán a la memoria caché
					 // como proceso de precarga.
            ivPreloadData = tupleHeapPreloadData.getPreLoadData(emd);

            ivOptimisticCallback = backingMap.getOptimisticCallback();
            ivBackingMapName = backingMap.getName();
            ivPartitionId = backingMap.getPartitionId();
            ivPartitionManager = backingMap.getPartitionManager();
            ivTransformer = backingMap.getObjectTransformer();
            Map preloadMap;

            if (ivPreloadData != null) {
                try {
                    ObjectMap map = session.getMap(ivBackingMapName);

                    // obtener preloadStatusMap para registrar el estado de precarga.
                    ObjectMap preloadStatusMap = session.getMap(ivPreloadStatusMapName);

                    // Nota: cuando se invoca este método preloadMap, se ha llamado
									// a checkPreloadStatus, se han establecido preloadStatus
									// y preloadedLastDataChunkIndex. Y
         // preloadStatus debe ser PARTIAL_PRELOAD_NEEDED
									// o FULL_PRELOAD_NEEDED que necesitarán una precarga de nuevo.

                    // Si el volumen de datos que debe precargarse es muy grande, los datos
                   // se suelen dividir en fragmentos y el proceso de precarga
									// procesará cada fragmento dentro de su propia transacción.. En este ejemplo sólo
         // se precargan unas pocas entradas, cada una de las cuales representa un fragmento.
                    // Por tanto, la precarga procesa una entrada en una transacción para simular
									//  la precarga de un fragmento.

                    Set entrySet = ivPreloadData.entrySet();
                    preloadMap = new HashMap();
                    ivMap = preloadMap;

                    // dataChunkIndex representa el fragmento de datos
									// en proceso
                    int dataChunkIndex = -1;
                    boolean shouldRecordPreloadStatus = false;
                    int numberOfDataChunk = entrySet.size();
                    System.out.println("    numberOfDataChunk to be preloaded = "
                       + numberOfDataChunk);

                    Iterator it = entrySet.iterator();
                    int whileCounter = 0;
                    while (it.hasNext()) {
                        whileCounter++;
                        System.out.println("preloadStatusKey = " + preloadStatusKey
														+ " , 
												whileCounter = " + whileCounter);

                        dataChunkIndex++;

                        // Si dataChunkIndex <= preloadedLastDataChunkIndex
                        // no es necesario realizar el proceso, porque lo ha precargado
											// antes otra JVM. Sólo se tiene que procesar dataChunkIndex
											// > preloadedLastDataChunkIndex
                        if (dataChunkIndex <= preloadedLastDataChunkIndex) {
                            System.out.println("ignore current dataChunkIndex =
															" + dataChunkIndex + " that has been previously
															preloaded.");
                            continue;
                        }

                        // Nota: este ejemplo simula un fragmento de datos como entrada.
                        // Cada clave representa un fragmento de datos por motivos de simplificación.
                        // Si el servidor o fragmento primario se ha detenido por razones desconocidas,
										  //  el estado de precarga que indica el progreso
										  // de la precarga debe estar disponible en preloadStatusMap. Una
            // réplica que pasa a ser primaria puede obtener el estado de precarga
											//  y determinar cómo realizar la precarga de nuevo.
                        // Nota: la grabación del estado de precarga debe estar en la misma
										  // transacción que incluir los datos en memoria caché; por lo que si se produce una
											// retrotracción o un error de la transacción, el estado de precarga grabado es el
											// estado real.

                        Map.Entry entry = (Entry) it.next();
                        Object key = entry.getKey();
                        Object value = entry.getValue();
                        boolean tranActive = false;

                        System.out.println("processing data chunk. map = " +
													this.ivBackingMapName + ", current dataChunkIndex = " +
													dataChunkIndex + ", key = " + key);

                        try {
                            shouldRecordPreloadStatus = false; // re-set to false
                            session.beginNoWriteThrough();
                            tranActive = true;

                            if (ivPartitionManager.getNumOfPartitions() == 1) {
                                // si sólo hay 1 partición, no es necesario realizar ninguna operación
														 // con ella.
                                // pase los datos a la memoria caché
                                map.put(key, value);
                                preloadMap.put(key, value);
                                shouldRecordPreloadStatus = true;
                            } else if (ivPartitionManager.getPartition(key) == ivPartitionId) {
                                // si la correlación está particionada, es necesario considerar que la
														 // clave de partición sólo precarga los datos que pertenecen
														 // a esta partición.
                                map.put(key, value);
                                preloadMap.put(key, value);
                                shouldRecordPreloadStatus = true;
                            } else {
                                // pasar por alto esta entrada porque no pertenece a
														 // esta partición.
                            }

                            if (shouldRecordPreloadStatus) {
                                System.out.println("record preload status. map = " +
																		this.ivBackingMapName + ", preloadStatusKey = " +
																		preloadStatusKey + ", current dataChunkIndex ="
+ dataChunkIndex);
                                if (dataChunkIndex == numberOfDataChunk) {
                                    System.out.println("record preload status. map = " +
																			  this.ivBackingMapName + ", preloadStatusKey = " +
																				preloadStatusKey + ", mark complete =" +
																				preloadCompleteMark);
                                    // significa que estamos en el último fragmento de datos,
                                    // si la operación se confirma correctamente,
                                    // el registro de la precarga se ha completado.
// En este punto, se considera que la precarga ha terminado.
                                    // use -99 como marca especial de estado completo de la precarga.

                                    preloadStatusMap.get(preloadStatusKey);

                                    // si una operación put sigue a una operación get se convierte
																 //  en update si get devuelve un objeto, de lo contrario será insert.
                                    preloadStatusMap.put(preloadStatusKey, new
																			Integer(preloadCompleteMark));

                                } else {
                                    // registrar dataChunkIndex precargado actual en
																 // preloadStatusMap una operación put sigue a una operación get se convierte
																 //  en update si get devuelve un objeto, de lo contrario
																 //  será insert.
                                    preloadStatusMap.get(preloadStatusKey);
                                    preloadStatusMap.put(preloadStatusKey, new
																		Integer(dataChunkIndex));
                                }
                            }

                            session.commit();
                            tranActive = false;

                            // para simular la precarga de un gran volumen de datos
                            // deje inactiva esta hebra durante 30 segundos.
                            // La aplicación real NO debe dejar inactiva esta hebra.
                            Thread.sleep(10000);

                        } catch (Throwable e) {
                            e.printStackTrace();
                            throw new LoaderException("preload failed with exception: " + e, e);
                        } finally {
                            if (tranActive && session != null) {
                                try {
                                    session.rollback();
                                } catch (Throwable e1) {
                                    // la precarga pasa por alto la excepción en la retrotracción
                                }
                            }
                        }
                    }

                    // En este punto, se considera que la precarga ha terminado.
                    // use -99 como marca especial de estado completo de la precarga.
                    // De esta manera se asegura de haber establecido la marca de finalización.
                    // Además, al crear particiones, cada partición desconoce cuándo
									//  es su último fragmento de datos. Por lo tanto, el bloque siguiente sirve como
									//  informe de finalización del estado general de la precarga.
                    System.out.println("Overall preload status complete -> record
												  preload status. map = " + this.ivBackingMapName + ",
												  preloadStatusKey = " + preloadStatusKey + ", mark complete =" +
													preloadCompleteMark);
                    session.begin();
                    preloadStatusMap.get(preloadStatusKey);
                    // si una operación put sigue a get, se convierte en update si
                    // la operación get devuelve un objeto,
                    // de lo contrario, será insert.
                    preloadStatusMap.put(preloadStatusKey, new Integer(preloadCompleteMark));
                    session.commit();

                    ivMap = preloadMap;
                } catch (Throwable e) {
                    e.printStackTrace();
                    throw new LoaderException("preload failed with exception: " + e, e);
                }
            }
        }
    }

Correlación de estado de precarga

Debe utilizar una correlación de estado de precarga para soporta la implementación de la interfaz ReplicaPreloadController. El método preloadMap siempre debe comprobar, en primer lugar, el estado de precarga almacenado en la correlación de estado de precarga y actualizar el estado de precarga en la correlación de estado de precarga siempre que se pasen datos a la memoria caché. El método checkPreloadStatus puede recuperar el estado de precarga de la correlación de estado de precarga, determinar el estado de precarga y devolver el estado al llamante. La correlación de estado de precarga debe estar en el mismo objeto mapSet que otras correlaciones que tienen cargadores de controlador de precarga de réplica.