Um utilitário de carga com um controlador de pré-carregamento de réplica é um utilitário de carga que implementa a interface ReplicaPreloadController além da interface do utilitário de carga.
A interface ReplicaPreloadController é projetada para fornecer uma maneira para uma réplica que se torna o shard primário saber se o shard primário anterior concluiu o processo de pré-carregamento. Se o pré-carregamento estiver parcialmente concluído, as informações para continuar onde o primário anterior parou são fornecidas. Com a implementação da interface ReplicaPreloadController, uma réplica que se torna o shard primário continua o processo de pré-carregamento onde o shard primário anterior parou e continua a conclusão do pré-carregamento geral.
Em um ambiente distribuído de WebSphere eXtreme Scale, um mapa pode ter réplicas e pode pré-carregar grandes volumes de dados durante a inicialização. O pré-carregamento é uma atividade do utilitário de carga e pode ocorrer apenas no mapa primário durante a inicialização. O pré-carregamento pode demorar muito para concluir, se um grande volume de dados for pré-carregado. Se o mapa primário concluiu uma grande parte dos dados pré-carregados, mas for parado por motivos desconhecidos durante a inicialização, uma réplica torna-se primária. Nessa situação, os dados de pré-carregamento que foram concluídos pela primária anterior são perdidos, pois a nova primária, em geral, desempenha um pré-carregamento incondicional. Com um pré-carregamento incondicional, a nova primária inicia o processo de pré-carregamento do início e os dados pré-carregados anteriormente são ignorados. Se desejar que o novo shard primário continue onde o shard primário anterior parou durante o processo de pré-carregamento, forneça um Utilitário de carga que implemente a interface ReplicaPreloadController. Para obter mais informações, consulte a documentação da API.
Para obter mais informações sobre os Loaders, consulte Utilitários de Carga. Se você estiver interessado em gravar um plug-in do Utilitário de Carga comum, consulte Criando um Utilitário de Carga.
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);
}
As seções a seguir abordam alguns dos métodos da interface Utilitário de Carga e ReplicaPreloadController.
Quando um Utilitário de Carga implementa a interface ReplicaPreloadController, o método checkPreloadStatus é chamado antes do método preloadMap durante a inicialização do mapa. O status de retorno deste método determina se o método preloadMap é chamado. Se este método retornar Status#PRELOADED_ALREADY, o método de pré-carregamento não é chamado. Caso contrário, o método preload será executado. Devido a este comportamento, este método deve servir como o método de inicialização do Utilitário de Carga. Você deve inicializar as propriedades do Utilitário de Carga neste método. Este método deve retornar o status correto, ou o pré-carregamento pode não funcionar conforme esperado.
public Status checkPreloadStatus(Session session, BackingMap backingMap) {
// When a loader implements ReplicaPreloadController interface,
// this method will be called before preloadMap method during
// map initialization. Whether the preloadMap method will be
// called depends on teh returned status of this method. So, this
// method also serve as Loader's initialization method. This method
// has to return the right staus, otherwise the preload may not
// work as expected.
// Note: must initialize this loader instance here.
ivOptimisticCallback = backingMap.getOptimisticCallback();
ivBackingMapName = backingMap.getName();
ivPartitionId = backingMap.getPartitionId();
ivPartitionManager = backingMap.getPartitionManager();
ivTransformer = backingMap.getObjectTransformer();
preloadStatusKey = ivBackingMapName + "_" + ivPartitionId;
try {
// get the preloadStatusMap to retrieve preload status that
// could be set by other JVMs.
ObjectMap preloadStatusMap = session.getMap(ivPreloadStatusMapName);
// retrieve last recorded preload data chunk index.
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;
}
A execução do método preloadMap depende do resultado retornado do método checkPreloadStatus. Se o método preloadMap for chamado, ele geralmente deve recuperar as informações de status do pré-carregamento a partir do mapa de status do pré-carregamento designado e determinar como continuar. A forma ideal seria o método preloadMap saber se o pré-carregamento foi parcialmente concluído e onde exatamente deve iniciar. Durante o pré-carregamento de dados, o método preloadMap deve atualizar o status do pré-carregamento no mapa de status do pré-carregamento designado. O status do pré-carregamento que é armazenado no mapa de status de pré-carregamento é recuperado pelo método checkPreloadStatus quando ele precisar verificar o status de pré-carregamento.
public void preloadMap(Session session, BackingMap backingMap)
throws LoaderException {
EntityMetadata emd = backingMap.getEntityMetadata();
if (emd != null && tupleHeapPreloadData != null) {
// The getPreLoadData method is similar to fetching data
// from database. These data will be push into cache as
// preload process.
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);
// obter o preloadStatusMap para registrar o status pré-carregado.
ObjectMap preloadStatusMap = session.getMap(ivPreloadStatusMapName);
// Note: when this preloadMap method is invoked, the
// checkPreloadStatus has been called, Both preloadStatus
// and preloadedLastDataChunkIndex have been set. And the
// preloadStatus must be either PARTIAL_PRELOAD_NEEDED
// or FULL_PRELOAD_NEEDED that will require a preload again.
// If large amount of data will be preloaded, the data usually
// is divided into few chunks and the preload process will
// process each chunk within its own tran. This sample only
// preload few entries and assuming each entry represent a chunk.
// so that the preload process an entry in a tran to simulate
// chunk preloading.
Set entrySet = ivPreloadData.entrySet();
preloadMap = new HashMap();
ivMap = preloadMap;
// The dataChunkIndex represent the data chunk that is in
// processing
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++;
// if the current dataChunkIndex <= preloadedLastDataChunkIndex
// no need to process, becasue it has been preloaded by
// other JVM before. only need to process dataChunkIndex
// > preloadedLastDataChunkIndex
if (dataChunkIndex <= preloadedLastDataChunkIndex) {
System.out.println("ignore current dataChunkIndex =
" + dataChunkIndex + " that has been previously
preloaded.");
continue;
}
// Note: This sample simulate data chunk as an entry.
// each key represent a data chunk for simplicity.
// If the primary server or shard stopped for unknown
// reason, the preload status that indicates the progress
// of preload should be available in preloadStatusMap. A
// replica that become a primary can get the preload status
// and determine how to preload again.
// Note: recording preload status should be in the same
// tran as putting data into cache; so that if tran
// rollback or error, the recorded preload status is the
// actual status.
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) {
// if just only 1 partition, no need to deal with
// partition.
// just push data into cache
map.put(key, value);
preloadMap.put(key, value);
shouldRecordPreloadStatus = true;
} else if (ivPartitionManager.getPartition(key) == ivPartitionId) {
// if map is partitioned, need to consider the
// partition key only preload data that belongs
// to this partition.
map.put(key, value);
preloadMap.put(key, value);
shouldRecordPreloadStatus = true;
} else {
// ignore this entry, because it does not belong to
// this partition.
}
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);
// means we are at the lastest data chunk, if commit
// successfully, record preload complete.
// at this point, the preload is considered to be done
// use -99 as special mark for preload complete status.
preloadStatusMap.get(preloadStatusKey);
// a put follow a get will become update if the get
// return an object, otherwise, it will be insert.
preloadStatusMap.put(preloadStatusKey, new Integer(preloadCompleteMark));
} else {
// record preloaded current dataChunkIndex into
// preloadStatusMap a put follow a get will become
// update if teh get return an object, otherwise, it
// will be insert.
preloadStatusMap.get(preloadStatusKey);
preloadStatusMap.put(preloadStatusKey, new Integer(dataChunkIndex));
}
}
session.commit();
tranActive = false;
// to simulate preloading large amount of data
// put this thread into sleep for 30 secs.
// The real app should NOT put this thread to sleep
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) {
// preload ignoring exception from rollback
}
}
}
}
// at this point, the preload is considered to be done for sure
// use -99 as special mark for preload complete status.
// this is a insurance to make sure the complete mark is set.
// besides, when partitioning, each partition does not know when
// is its last data chunk. so the following block serves as the
// overall preload status complete reporting.
System.out.println("Overall preload status complete -> record preload status. map = " + this.ivBackingMapName + ",
preloadStatusKey = " + preloadStatusKey + ", mark complete =" +
preloadCompleteMark);
session.begin();
preloadStatusMap.get(preloadStatusKey);
// a put follow a get will become update if teh get return an object,
// caso contrário, ele será inserido.
preloadStatusMap.put(preloadStatusKey, new Integer(preloadCompleteMark));
session.commit();
ivMap = preloadMap;
} catch (Throwable e) {
e.printStackTrace();
throw new LoaderException("preload failed with exception: " + e, e);
}
}
}
}
É necessário utilizar um mapa de status do pré-carregamento para suportar a implementação da interface ReplicaPreloadController. O método preloadMap deve sempre verificar primeiro o status do pré-carregamento armazenado no mapa de status do pré-carregamento e atualizar o status do pré-carregamento no mapa de status do pré-carregamento sempre que ele enviar os dados para o cache. O método checkPreloadStatus pode recuperar o status do pré-carregamento a partir do mapa de status do pré-carregamento, determinar o status de pré-carregamento e retornar o status para o responsável pela chamada. O mapa de status do pré-carregamento deve estar no mesmo mapSet que outros mapas que possuem utilitários de carga do controlador de pré-carregamento de réplica.