Gravando um Utilitário de Carga com um Controlador de Pré-carregamento de Réplica

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.

A interface ReplicaPreloadController possui a seguinte definição:
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.

Método checkPreloadStatus

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;
    }

Método preloadMap

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);
                }
            }
        }
    }

Mapa de Status do Pré-carregamento

É 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.