Ein Loader mit einem Preload-Controlle für Replikate ist ein Loader, der die Schnittstelle ReplicaPreloadController zusätzlich zur Schnittstelle "Loader" implementiert.
Die Schnittstelle "ReplicaPreloadController" bietet einem Replikat, das zum primären Shard wird, die Möglichkeit festzustellen, ob das vorherige primäre Shard den Preload-Prozess (Prozess für vorheriges Laden) vollständig abgeschlossen hat. Wenn der Preload-Prozess nur teilweise abgeschlossen ist, werden Informationen bereitgestellt, anhand derer das neue primäre Replikat die Verarbeitung dort fortsetzen kann, wo das vorherige Replikat aufgehört hat. Mit der Implementierung der Schnittstelle "ReplicaPreloadController" setzt ein Replikat, das zum primären Replikat wird, den Preload-Prozess dort fort, wo das vorherige Replikat aufgehört hat, und beendet des gesamten Preload-Prozess.
In einer verteilten WebSphere eXtreme Scale-Umgebung kann eine Map Replikate haben und ein hohes Datenvolumen während der Initialisierung vorher laden. Der Preload-Prozess ist eine Loader-Aktivität und findet nur in der primären Map während der Initialisierung statt. Die Ausführung des Preload-Prozesses kann lange dauern, wenn ein hohes Datenvolumen vorher geladen werden muss. Wenn die primäre Map bereits einen großen Teil der Preload-Daten geladen hat, aber dann aus einem unbekannten Grund während der Initialisierung gestoppt wird, wird ein Replikat zur primären Map. In dieser Situation gehen die von der vorherigen Map bereits geladenen Preload-Daten verloren, weil die neue primäre Map normalerweise einen unbedingten Preload durchführt. Bei einem unbedingten Preload startet die neue primäre Map den Preload-Prozess von vorn, und die bereits geladenen Daten werden ignoriert. Wenn die neue primäre Map dort weitermachen soll, wo die vorherige primäre Map während des Preload-Prozesses aufgehört hat, stellen Sie einen Loader bereit, der die Schnittstelle "ReplicaPreloadController" implementiert. Weitere Informationen finden Sie in der API-Dokumentation.
Einzelheiten zu Loadern finden Sie in Loader. Wenn Sie ein reguläres Loader-Plug-in schreiben möchten, lesen Sie den Abschnit Loader schreiben.
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);
}
In den folgenden Abschnitten werden einige Methoden der Schnittstellen "Loader" und "ReplicaPreloadController" beschrieben.
Wenn ein Loader die Schnittstelle "ReplicaPreloadController" implementiert, wird während der Map-Initialisierung die Methode "checkPreloadStatus" vor der Methode "preloadMap" aufgerufen. Der Rückkehrstatus dieser Methode bestimmt, ob die Methode "preloadMap" aufgerufen wird. Wenn diese Methode Status#PRELOADED_ALREADY zurückgibt, wird die Preload-Methode nicht aufgerufen. Andernfalls wird die Methode "preload" aufgerufen. Aufgrund dieses Verhaltens sollte diese Methode als Methode für die Loader-Initialisierung dienen. Sie müssen in dieser Methode die Loader-Eigenschaften initialisieren. Diese Methode muss den richtigen Status zurückgeben, oder der Preload-Prozess funktioniert nicht wie erwartet.
public Status checkPreloadStatus(Session session, BackingMap backingMap) {
// Wenn ein Loader die Schnittstelle "ReplicaPreloadController" implementiert,
// wird diese Methode während der Map-Initialisierung vor der Methode
// "preloadMap" aufgerufen. Ob die Methode "preloadMap" aufgerufen wird,
// richtet sich nach dem Status, den diese Methode zurückgibt. // Deshalb
// dient diese Methode auch als Methode für die Initialisierung des Loaders.
// Diese Methode muss den richtigen Status zurückgeben, da der Preload-Prozess
// ansonsten nicht wie erwartet funktioniert.
// Anmerkung: Hier muss die Loader-Instanz initialisiert werden.
ivOptimisticCallback = backingMap.getOptimisticCallback();
ivBackingMapName = backingMap.getName();
ivPartitionId = backingMap.getPartitionId();
ivPartitionManager = backingMap.getPartitionManager();
ivTransformer = backingMap.getObjectTransformer();
preloadStatusKey = ivBackingMapName + "_" + ivPartitionId;
try {
// preloadStatusMap abrufen, um den Preload-Status abzurufen, der von anderen JVMs
// gesetzt werden kann.
ObjectMap preloadStatusMap = session.getMap(ivPreloadStatusMapName);
// Index des zuletzt aufgezeichneten Preload-Datenblocks abrufen.
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;
}
Die Ausführung der Methode "preloadMap" ist von dem von der Methode "checkPreloadStatus" zurückgegebenen Ergebnis abhängig. Wenn die Methode "preloadMap" aufgerufen wird, muss sie gewöhnlich Informationen zum Preload-Status aus der angegebenen Preload-Status-Map abrufen und die weitere Vorgehensweise bestimmen. Idealerweise sollte die Methode "preloadMap" wissen, ob der Preload-Prozess teilweise abgeschlossen wurde und wo genau begonnen werden muss. Während des Daten-Preloads muss die Methode "preloadMap" den Preload-Status in der angegebenen Preload-Status-Map aktualisieren. Der Preload-Status, der in der Preload-Status-Map gespeichert ist, wird von der Methode "checkPreloadStatus" abgerufen, wenn diese den Preload-Status überprüfen muss.
public void preloadMap(Session session, BackingMap backingMap)
throws LoaderException {
EntityMetadata emd = backingMap.getEntityMetadata();
if (emd != null && tupleHeapPreloadData != null) {
// Die Methode "getPreLoadData" gleicht dem Abruf von Daten aus der Datenbank.
// Diese Daten werden als Preload-Prozess mit Push in den Cache übertragen.
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);
// preloadStatusMap abrufen, um den Preload-Status aufzuzeichnen.
ObjectMap preloadStatusMap = session.getMap(ivPreloadStatusMapName);
// Anmerkung: Wenn die Methode preloadMap aufgerufen wird, wurde
// checkPreloadStatus aufgerufen, und preloadStatus und
// preloadedLastDataChunkIndex wurden gesetzt.
// Der preloadStatus muss PARTIAL_PRELOAD_NEEDED oder
// FULL_PRELOAD_NEEDED sein. Diese erfordern einen erneuten Preload.
// Wenn sehr viele Daten vorher geladen werden, werden die Daten gewöhnlich
// in Blöcke eingeteilt, und der Preload-Prozess verarbeitet jeden Block in
// einer gesonderten Transaktion. In diesem Beispiel werden nur ein paar
// Einträge vorher geladen, und jeder Eintrag wird als Block betrachtet.
// Der Preload-Prozess verarbeitet also jeweils einen Eintrag in einer
// einer Transaktion, um das vorherige Laden von Datenblöcken zu simulieren.
Set entrySet = ivPreloadData.entrySet();
preloadMap = new HashMap();
ivMap = preloadMap;
// dataChunkIndex stellt den Datenblock dar, der verarbeitet wird.
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++;
// Wenn aktueller dataChunkIndex <= preloadedLastDataChunkIndex
// ist, ist keine Verarbeitung erforderlich, weil der Datenblock
// bereits von einer anderen JVM vorher geladen wurde. Es muss nur
// dataChunkIndex verarbeitet werden.
// > preloadedLastDataChunkIndex
if (dataChunkIndex <= preloadedLastDataChunkIndex) {
System.out.println("ignore current dataChunkIndex =
" + dataChunkIndex + " that has been previously
preloaded.");
continue;
}
// Anmerkung: Dieses Beispiel simuliert einen Datenblock mit einem Eintrag.
// Jeder Schlüssel stellt zur Einfachheit einen Datenblock dar.
// Wenn der primäre Server oder das primäre Shard aus einem unbekannten
// Grund gestoppt wird, muss der Preload-Status, der den Fortschritt des
// Preload-Prozesses anzeigt, in der preloadStatusMap verfügbar sein.
// Ein Replikat, das zu einem primären Shard wird, kann den Preload-Status
// abrufen und bestimmen, wird der erneute Preload-Prozess durchgeführt
// werden muss.
// Anmerkung: Der Preload-Status sollte in derselben Transaktion aufgezeichnet
// werden, in der auch die Daten in den Cache übertragen werden, so dass
// der aufgezeichnete Preload-Status der tatsächliche Status ist,
// falls ein Rollback durchgeführt werden muss oder ein Fehler auftritt.
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) {
// Wenn nur eine einzige Partition vorhanden ist, Partitionierung übergehen.
// Die Daten nur mit Push in den Cache übertragen.
map.put(key, value);
preloadMap.put(key, value);
shouldRecordPreloadStatus = true;
} else if (ivPartitionManager.getPartition(key) == ivPartitionId) {
// Wenn die Map partitioniert ist, muss der Partitionsschlüssel
// berücksichtigt werden.
// Nur Daten vorher laden, die zu dieser Partition gehören.
map.put(key, value);
preloadMap.put(key, value);
shouldRecordPreloadStatus = true;
} else {
// Entität ignorieren, weil sie nicht zu dieser Partition gehört.
}
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);
// Bedeutet, dass dies der letzte Datenblock ist. Bei erfolgreicher
// Festschreibung, aufzeichnen, dass Preload abgeschlossen ist.
// Jetzt ist der Preload abgeschlossen.
// -99 als Sondermarkierung für den Status "Preload abgeschlossen" verwenden.
preloadStatusMap.get(preloadStatusKey);
// Ein put nach einem get wird zu update, wenn get ein Objekt zurückgibt,
// andernfalls wird es zu insert.
preloadStatusMap.put(preloadStatusKey, new Integer(preloadCompleteMark));
} else {
// Ein put nach einem get wird zu update, wenn get ein Objekt zurückgibt,
// andernfalls wird es zu insert.
preloadStatusMap.get(preloadStatusKey);
preloadStatusMap.put(preloadStatusKey, new Integer(dataChunkIndex));
}
}
session.commit();
tranActive = false;
// Zum Simulieren eines Preloads großer Datenmengen
// diesen Thread für 30 Sekunden inaktivieren.
// Die echte Anwendung darf diesen Thread NICHT inaktivieren.
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 durchführen und Ausnahme der Rollback-Operation ignorieren
}
}
}
}
// Jetzt ist der Preload-Prozess wirklich abgeschlossen.
// -99 als Sondermarkierung für den Status "Preload abgeschlossen" verwenden.
// Dies gewährleistet, dass die Abschlussmarkierung gesetzt wird.
// Außerdem weiß bei der Partitionierung nicht jede Partition, wann es sich
// um den letzten Datenblock handelt. Deshalb dient der folgende Block als
// Bericht für den Bericht, dass der gesamte Preload-Prozess abgeschlossen ist.
System.out.println("Overall preload status complete -> record
preload status. map = " + this.ivBackingMapName + ",
preloadStatusKey = " + preloadStatusKey + ", mark complete =" +
preloadCompleteMark);
session.begin();
preloadStatusMap.get(preloadStatusKey);
// Ein put nach einem get wird zu update, wenn get ein Objekt zurückgibt,
// andernfalls wird es zu 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);
}
}
}
}
Sie müssen eine Map für den Preload-Status für die Unterstützung der ReplicaPreloadController-Schnittstellenimplementierung verwenden. Die Methode "preloadMap" muss immer zuerst den Preload-Status in der Preload-Status-Map prüfen und den Preload-Status in der Preload-Status-Map aktualisieren, wenn sie Daten mit Push in den Cache überträgt. Die Methode "checkPreloadStatus" kann den Preload-Status aus der Preload-Status-Map abrufen, den Preload-Status bestimmen und den Status an den Aufrufenden zurückgeben. Die Preload-Status-Map muss sich in demselben MapSet befinden wie die anderen Maps, die Loader mit einem Preload-Controller für Replikate enthalten.