Sofern alle Nodes in einem Cluster korrekt verbunden sind, werden alle Kopien eines Eintrags konsistent behandelt. Im Fall eines Netzwerkfehlers oder von Ausfällen von Nodes ist dies aber nicht sichergestellt. Entsprechend des CAP-Theorems [1] ist es nicht möglich, gleichzeitig die Verfügbarkeit und die Konsistenz zu gewährleisten. In diesem Artikel stelle ich vor, wie mit dem Partition Handling entschieden werden kann, ob die Konsistenz Vorrang hat, und welche Auswirkungen das auf die Anwendung hat.
In einer produktiven Umgebung ist es wünschenswert, dass Probleme wie Abstürze, Strom- oder Netzwerkausfall und Ressourcenengpässe nicht vorkommen. Allerdings sollte man immer einen Plan B für solche Situationen haben. Mit dem hier vorgestellten Feature ist es möglich, pro Cache festzulegen, ob im Fall unerwarteter Ausfälle einzelner Nodes die Verfügbarkeit des Caches eingeschränkt wird, um die Konsistenz der Daten zu gewährleisten. Allerdings erfordert das unter Umständen Anpassungen in der Anwendung, um auf die dann geworfene Exception zu reagieren.
Bis zu diesen Versionen wurden im Fall von Clusterproblemen keine besonderen Aktionen gestartet. Das führte dazu, dass in den Partitionen die Daten unabhängig voneinander geändert werden konnten und bei einem anschließenden Merge dadurch Inkonsistenzen entstanden.
Nach einem Split – auch Split Brain genannt – wurde in jedem einzelnen Teil – auch Partition genannt – der Cache neu organisiert. Bei einem Cache im Mode Distributed wurde der ConsistentHash neu berechnet und entsprechend der Anzahl von Kopien numOwners wurden die Einträge repliziert. Damit sind eventuell einige Einträge in der einen Partition nicht mehr vorhanden, andere sind jetzt in mehreren Partitions unabhängig voneinander gespeichert. Im Fall eines Merge von Partitions wird anhand des dann neu berechneten ConsistentHash ein Eintrag auf allen Nodes gelöscht, wo dieser nicht mehr benötigt wird. Dadurch kommt es vor, dass ein eigentlich aktueller Eintrag entfernt wird oder mehrere inkonsistente Einträge im Cache verbleiben.
Für einen Cache im Mode Replicated wird keine Umverteilung vorgenommen, die Einträge verbleiben so wie vor dem Merge, und damit sind die Einträge, die während des Splits geändert wurden, so lange unterschiedlich, bis sie nochmals geändert oder gelöscht werden. Einträge, die während des Splits gelöscht wurden, können nach dem Merge als verwaiste Einträge auf manchen Knoten verbleiben.
Hier wurden einige Änderungen vorgenommen, um eine bessere Konsistenz zu gewährleisten; allerdings kann es auch hierbei immer noch zu Datenverlusten kommen. Wird keine Änderung an der Konfiguration vorgenommen, so ist dies das Verhalten ab Infinispan 7.1 bzw. JDG 6.4.Knoten, die eine Partition verlassen, werden aus der View gelöscht, ohne Rücksicht darauf, ob sie heruntergefahren wurden, abgestürzt oder anderweitig nicht erreichbar sind. Bei einem Netzwerkproblem führt das dazu, dass mehrere Partitions des Clusters die Werte eines Keys unterschiedlich ändern können. Updates, die direkt vor oder während des Splits vorgenommen wurden, können eventuell nur teilweise, d. h. auf einem Teil der Knoten, ausgeführt werden.
Bei einem Merge wird die Partition mit der höheren internen Topology-ID als Master verwendet, andere Partitionen werden komplett gelöscht und die Einträge aus der Master-Partition repliziert. Es besteht keine Möglichkeit, die Einträge individuell zusammenzuführen.
Fehlt das Element für Partition Handling, zum Beispiel wenn die Konfiguration von dem vorhergehenden Release übernommen wurde, so ist das Handling deaktiviert. Listing 1 zeigt ein Beispiel für die Konfiguration mit aktiviertem Partition Handling für einen Server (clustered.xml). Listing 2 zeigt eine äquivalente XML-Konfiguration für den Embedded Mode. In Listing 3 ist der notwendige Java-Quellcode für die programmatische Konfiguration eines Caches dargestellt.
Für replicated Caches wurde das Partition Handling etwas später eingeführt [2], und aufgrund eines Bugs [3] wird bei einem replicated Cache eine Warnung protokolliert, dass die Konfiguration ignoriert wird, obwohl das Partition Handling korrekt eingeschaltet wurde. Dies wurde mit Infinispan 7.2.2 oder JDG 6.5 behoben.
Listing 1
<subsystem xmlns="urn:infinispan:server:core:6.2" default-cachecontainer="clustered">
<cache-container name="clustered" default-cache="default" statistics="true">
<distributed-cache name="default" mode="SYNC" segments="20" owners="2" remote-timeout="30000" start="EAGER">
<partition-handling enabled="true" />
<locking isolation="READ_COMMITTED" acquire-timeout="30000" concurrency-level="1000" striping="false"/>
<transaction mode="NONE"/>
</distributed-cache>
</cache-container>
</subsystem>
Listing 2
<namedCache name="DistPHCache">
<clustering mode="distribution">
<partitionHandling enabled="true" />
<sync />
</clustering>
....
</namedCache>
Listing 3
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.clustering().partitionHandling().enabled(true);
Bei aktiviertem Partition...