Vorabladen von Maps

Maps können so genannte Loader (Ladeprogramme) zugeordnet werden. Ein Loader wird verwendet, um Objekte abzurufen, wenn diese nicht in der Map gefunden werden (Cachefehler), und um Änderungen in ein Back-End zu schreiben, wenn eine Transaktion festgeschrieben wird. Loader können auch für das Vorabladen von Daten (Preload) in eine Map verwendet werden. Die Methode preloadMap der Schnittstelle "Loader" wird für jede Map aufgerufen, wenn die zugehörige Partition im MapSet zu einem primären Shard wird. Die Methode preloadMap wird nicht für Replikate aufgerufen. Sie versucht, alle geplanten referenzierten Daten über die bereitgestellte Sitzung aus dem Back-End in die Map zu laden. Die jeweilige Map wird mit dem Argument "BackingMap" angegeben, das an die Methode preloadMap übergeben wird.

void preloadMap(Session session, BackingMap backingMap) throws LoaderException;

Vorabladen in einem partitionierten MapSet

Maps können in N Partitionen partitioniert werden. Deshalb können Maps auf mehrere Server verteilt werden, wobei jeder Eintrag mit einem Schlüssel gekennzeichnet wird, der nur in einem einzigen dieser Server gespeichert wird. Sehr große Maps können in einem Datengrid verwaltet werden, weil die Anwendung nicht mehr durch die Heapspeichergröße einer einzigen Java Virtual Machine (JVM) beschränkt ist, die alle Einträge einer Map enthält. Anwendungen, die den Preload-Prozess mit der Methode preloadMap der Loader ausführen möchten, müssen den Teil der Daten angeben, die vorab geladen werden sollen. Es ist immer eine feste Anzahl an Partitionen vorhanden. Sie können diese Zahl anhand des folgenden Codebeispiels bestimmen:

int numPartitions = backingMap.getPartitionManager().getNumOfPartitions();
int myPartition = backingMap.getPartitionId();
Dieses Codebeispiel zeigt, wie eine Anwendung den Teil der Daten angeben kann, der vorab aus der Datenbank geladen werden soll. Anwendungen müssen diese Methoden auch dann verwenden, wenn die Map zunächst nicht partitioniert ist. Diese Methoden bieten Flexibilität: Wenn die Map später von den Administratoren partitioniert wird, funktioniert der Loader weiterhin ordnungsgemäß.

Die Anwendung muss Abfragen absetzen, um den Teil myPartition aus dem Back-End abzurufen. Wenn eine Datenbank verwendet wird, kann es unter Umständen einfacher sein, eine Spalte mit der Partitionskennung für einen bestimmten Datensatz zu haben, sofern es keine natürliche Abfrage gibt, mit der die Daten in der Tabelle einfach partitioniert werden können.

Ein Beispiel für die Implementierung einr Loader-Schnittstelle für ein repliziertes Datengrid finden Sie unter Loader mit einem Preload-Controller für Replikate schreiben.

Leistung

Die Preload-Implementierung kopiert Daten aus dem Back-End in die Map, indem sie mehrere Objekte in der Map in einer einzigen Transaktion speichert. Die optimale Anzahl der pro Transaktion zu speichernden Datensätze richtet sich nach mehreren Faktoren, einschließlich der Komplexität und der Größe. Wenn die Transaktion beispielsweise Blöcke mit mehr als 100 Einträgen enthält, nehmen die Leistungsgewinne ab, wenn Sie die Anzahl der Einträge erhöhen. Zur Bestimmung der optimalen Anzahl beginnen Sie mit 100 Einträgen, und erhöhen Sie dann die Anzahl, bis keine Leistungsgewinne mehr zu verzeichnen sind. Mit größeren Transaktionen kann eine bessere Replikationsleistung erzielt werden. Denken Sie daran, dass der Preload-Code nur im primären Shard ausgeführt wird. Die vorab geladenen Daten werden über das primäre Shard in allen Replikaten repliziert, die online sind.

Vorabladen von MapSets

Wenn die Anwendung ein MapSet mit mehreren Maps verwendet, hat jede Map einen eigenen Loader. Jeder Loader besitzt eine Preload-Methode. Alle Maps werden nacheinander vom Datengrid geladen. Es kann effizienter sein, den Preload-Prozess für die Maps so zu gestalten, dass eine einzige Map als Map bestimmt wird, in die die Daten vorab geladen werden. Dieser Prozess ist eine Anwendungskonvention. Beispiel: Die beiden Maps "department" (Abteilung) und "employee" (Mitarbeiter) könnten beide den Loader der Map "department" verwenden. Bei dieser Prozedur wird über Transaktionen gewährleistet, dass in dem Fall, dass eine Anwendung eine Abteilung abrufen möchte, sich die Mitarbeiter für diese Abteilung im Cache befinden. Wenn der Loader der Map "department" eine Abteilung vorab aus dem Back-End lädt, ruft er auch die Mitarbeiter für diese Abteilung ab. Das department-Objekt und die zugehörigen employee-Objekte werden dann der Map in einer einzigen Transaktion hinzugefügt.

Wiederherstellbares Vorabladen

Einige Kunden haben sehr große Datenmengen, die zwischengespeichert werden müssen. Das Vorabladen dieser Daten kann sehr zeitaufwendig sein. Manchmal muss das Vorabladen abgeschlossen sein, bevor die Anwendung online gehen kann. In diesem Fall können Sie von einem wiederherstellbaren Vorabladen profitieren. Angenommen, es gibt Millionen Datensätze, die vorab geladen werden müssen. Die Daten werden vorab in das primäre Shard geladen, und der Prozess scheitert bei Datensatz 800.000. Normalerweise löscht das als neue primäre Shard ausgewählte Replikat den Replikationsstatus und beginnt von vorne. eXtreme Scale kann eine ReplicaPreloadController-Schnittstelle verwenden. Der Loader für die Anwendung muss auch die Schnittstelle ReplicaPreloadController implementieren. Das folgende Beispiel fügt dem Loader eine einzige Methode hinzu: Status checkPreloadStatus(Session session, BackingMap bmap);. Diese Methode wird von der Laufzeitumgebung von eXtreme Scale aufgerufen, bevor die Methode "preload" der Schnittstelle "Loader" aufgerufen wird. eXtreme Scale prüft das Ergebnis dieser Methode (Status), um das Verhalten festzulegen, wenn ein Replikat in ein primäres Shard hochgestuft werden muss.

Tabelle 1. Statuswert und Antwort
Zurückgegebener Statuswert Antwort von eXtreme Scale
Status.PRELOADED_ALREADY eXtreme Scale ruft die Methode "preload" gar nicht auf, weil dieser Statuswert anzeigt, dass der Preload-Prozess für die Map bereits vollständig abgeschlossen ist.
Status.FULL_PRELOAD_NEEDED eXtreme Scale löscht die Map und ruft ganz regulär die Methode "preload" auf.
Status.PARTIAL_PRELOAD_NEEDED eXtreme Scale belässt die Map im aktuellen Zustand und ruft die Methode "preload" auf. Diese Strategie ermöglicht dem Loader der Anwendung, den Vorabladeprozess (Preload) an der Stelle fortzusetzen, an der er zuvor abgebrochen wurde.

Es ist logisch, dass ein primäres Shard beim Vorabladen von Daten in die Map einen Status in einer Map im MapSet hinterlassen muss, das repliziert wird, so dass das Replikat den zurückzugebenden Status bestimmen kann. Sie können dazu eine zusätzliche Map verwenden, die z. B. den Namen RecoveryMap hat. Diese RecoveryMap-Map muss zu demselben MapSet gehören, das vorab geladen wird, um sicherzustellen, dass die replizierte Map mit den vorab geladenen Daten konsistent ist. Eine empfohlene Implementierung folgt.

Während der Preload-Prozess die einzelnen Datensatzblöcke festschreibt, aktualisiert er im Rahmen dieser Transaktion auch einen Zähler oder Wert in der RecoveryMap-Map. Die vorab geladenen Daten und die Daten aus der RecoveryMap-Map werden automatisch in den Replikaten repliziert. Wenn das Replikat in ein primäres Shard hochgestuft wird, kann es anhand der RecoveryMap-Map prüfen, was passiert ist.

Die RecoveryMap-Map kann einen einzigen Eintrag mit dem Statusschlüssel enthalten. Wenn kein Objekt für diesen Schlüssel vorhanden ist, müssen Sie eine vollständige Operation preload (checkPreloadStatus returns FULL_PRELOAD_NEEDED) durchführen. Wenn ein Objekt für diesen Statusschlüssel vorhanden ist und der Wert COMPLETE lautet, ist der Preload-Prozess abgeschlossen, und die Methode checkPreloadStatus gibt PRELOADED_ALREADY zurück. Andernfalls zeigt das Wertobjekt an, wo der Vorabladeprozess (Preload) erneut gestartet werden muss, und die Methode checkPreloadStatus gibt PARTIAL_PRELOAD_NEEDED zurück. Der Loader kann den Wiederherstellungspunkt in einer Instanzvariablen speichern, so dass der Loader beim Aufruf der Methode "preload" diesen Ausgangspunkt kennt. Die RecoveryMap-Map kann auch einen Eintrag pro Map enthalten, wenn jede Map gesondert vorab geladen wird.

Handhabung der Wiederherstellung im synchronen Replikationsmodus mit einem Loader

Die Laufzeitumgebung von eXtreme Scale ist so konzipiert, dass festgeschriebene Daten beim Ausfall des primären Shards nicht verloren gehen. Im folgenden Abschnitt werden der verwendeten Algorithmen beschrieben. Diese Algorithmen gelten nur, wenn eine Replikationsgruppe die synchrone Replikation verwendet. Ein Loader ist optional.

Die Laufzeitumgebung von eXtreme Scale kann so konfiguriert werden, dass alle Änderungen in einem primären Shard synchron in den Replikaten repliziert werden. Wenn ein synchrones Replikat verteilt wird, erhält es eine Kopie der vorhandenen Daten im primären Shard. In dieser Zeit empfängt das primäre Shard weiterhin Transaktionen und kopiert sie asynchron in das Replikat. Das Replikat wird in dieser Zeit nicht als online eingestuft.

Wenn das Replikat denselben Stand wie das primäre Shard hat, wechselt das Replikat in den Peer-Modus, und die synchrone Replikation beginnt. Jede im primären Shard festgeschriebene Transaktion wird an die synchronen Replikate gesendet, und das primäre Shard wartet auf eine Antwort jedes Replikats. Eine synchrone Festschreibungsfolge mit einem Loader im primären Shard setzt sich aus den folgenden Schritten zusammen:

Tabelle 2. Festschreibungsfolge im primären Shard
Schritt mit Loader Schritt ohne Loader
Sperren für Einträge abrufen Identisch
Änderung mit Flush an den Loader übertragen Nulloperation
Änderungen im Cache speichern Identisch
Änderungen an Replikate senden und auf Bestätigung warten Identisch
Festschreibung im Loader über das TransactionCallback-Plug-in Methode "commit" des Plug-ins wird zwar aufgerufen, führt aber keine Aktion aus
Sperren für Einträge freigeben Identisch

Beachten Sie, dass die Änderungen an das Replikat gesendet werden, bevor sie im Loader festgeschrieben werden. Um zu bestimmen, wann die Änderungen im Replikat festgeschrieben werden, ändern Sie diese Folge. Initialisieren Sie während der Initialisierung die Transaktionslisten im primären Shard wie folgt:

CommitedTx = {}, RolledBackTx = {}

Für eine synchrone Commit-Verarbeitung verwenden Sie die folgende Folge:

Tabelle 3. Synchrone Commit-Verarbeitung
Schritt mit Loader Schritt ohne Loader
Sperren für Einträge abrufen Identisch
Änderung mit Flush an den Loader übertragen Nulloperation
Änderungen im Cache speichern Identisch
Änderungen mit einer festgeschriebenen Transaktion senden, Rollback der Transaktion an das Replikat durchführen und auf Bestätigung warten Identisch
Liste festgeschriebener und rückgängig gemachter Transaktionen löschen Identisch
Festschreibung im Loader über das TransactionCallBack-Plug-in Methode des TransactionCallBack-Plug-ins wird weiterhin aufgerufen, führt aber gewöhnlich keine Aktion aus
Bei erfolgreicher Festschreibung Transaktion der Liste festgeschriebener Transaktionen hinzufügen, andernfalls der Liste rückgängig gemachter Transaktionen hinzufügen Nulloperation
Sperren für Einträge freigeben Identisch

Für die Replikatverarbeitung verwenden Sie die folgende Folge:

  1. Änderungen empfangen
  2. Alle empfangenen Transaktionen in der Liste festgeschriebener Transaktionen festschreiben
  3. Alle empfangenen Transaktionen in der Liste rückgängig gemachter Transaktionen rückgängig machen
  4. Transaktion oder Sitzung starten
  5. Änderung auf die Transaktion oder Sitzung anwenden
  6. Transaktion oder Sitzung in der Liste offener Transaktionen speichern
  7. Antwort zurücksenden

Beachten Sie, dass im Replikat keine Loader-Interaktionen stattfinden, während sich das Replikat im Replikatmodus befindet. Das primäre Shard muss alle Änderungen mit Push über den Loader übertragen. Das Replikat ändert keine Daten. Dieser Algorithmus hat den Nebeneffekt, dass das Replikat immer die Transaktionen hat, diese aber erst festgeschrieben werden, wenn die nächste primäre Transaktion den Festschreibungsstatus dieser Transaktionen sendet. Erst dann werden die Transaktionen im Replikat festgeschrieben oder rückgängig gemacht. Bis dahin sind die Transaktionen nicht festgeschrieben. Sie können einen Zeitgeber für das primäre Shard hinzufügen, der das Transaktionsergebnis nach kurzer Zeit (ein paar Sekunden) sendet. Dieser Zeitgeber verringert, schließt aber das Risiko veralteter Daten in diesem Zeitfenster nicht ganz aus. Veraltete Daten sind nur dann ein Problem, wenn der Lesemodus für Replikate verwendet wird. Andernfalls haben die veralteten Daten keine Auswirkungen auf die Anwendung.

Wenn das primäre Shard ausfällt, ist es wahrscheinlich, dass einige Transaktionen zwar im primären Shard festgeschrieben oder rückgängig gemacht wurden, aber die Nachricht mit diesen Ergebnissen nicht mehr an das Replikat gesendet werden konnte. Wenn ein Replikat als neues primäres Shard hochgestuft wird, ist eine der ersten Aktionen die Behandlung dieser Bedingung. Jede offene Transaktion wird erneut für die Map-Gruppe des neuen primären Shards ausgeführt. Wenn ein Loader vorhanden ist, wird die erste Transaktion an den Loader übergeben. Diese Transaktionen werden in strikter First In/First Out-Reihenfolge angewendet. Wenn eine Transaktion scheitert, wird sie ignoriert. Sind drei Transaktionen, A, B und C, offen, kann A festgeschrieben, B rückgängig gemacht und C ebenfalls festgeschrieben werden. Keine der Transaktionen hat Auswirkung auf die anderen. Gehen Sie davon aus, dass sie voneinander unabhängig sind.

Ein Loader kann eine geringfügig andere Logik verwenden, wenn er sich im Modus für Fehlerbehebung durch Funktionsübernahme und nicht im normalen Modus befindet. Der Loader kann problemlos feststellen, wann er sich im Modus für Fehlerbehebung durch Funktionsübernahme befindet, indem er die Schnittstelle ReplicaPreloadController implementiert. Die Methode checkPreloadStatus wird erst aufgerufen, wenn der Loader wieder aus dem Modus für Fehlerbehebung durch Funktionsübernahme in den normalen Modus wechselt. Wenn die Methode "apply" der Schnittstelle "Loader" vor der Methode checkPreloadStatus aufgerufen wird, handelt es sich deshalb um eine Wiederherstellungstransaktion. Nach dem Aufruf der Methode checkPreloadStatus ist die Fehlerbehebung durch Funktionsübernahme abgeschlossen.