Replizierte Grains

Manchmal können mehrere Instanzen desselben Grains aktiv sein, beispielsweise beim Betrieb eines Multiclusters und der Verwendung von OneInstancePerClusterAttribute. Das JournaledGrain wurde entwickelt, um replizierte Instanzen reibungslos zu unterstützen. Es beruht auf Protokollkonsistenzanbietern, die die notwendigen Protokolle ausführen, um sicherzustellen, dass alle Instanzen über dieselbe Abfolge von Ereignissen übereinstimmen. Dabei werden insbesondere die folgenden Aspekte berücksichtigt:

  • Konsistente Versionen: Alle Versionen des Grainzustands (mit Ausnahme von vorläufigen Versionen) basieren auf derselben globalen Abfolge von Ereignissen. Insbesondere, wenn zwei Instanzen die gleiche Versionsnummer sehen, dann sehen sie den gleichen Zustand.

  • Race-Ereignisse: Mehrere Instanzen können gleichzeitig ein Ereignis auslösen. Der Konsistenzgeber löst diese Racebedingung auf und sorgt dafür, dass alle die gleiche Reihenfolge einhalten.

  • Benachrichtigungen/Reaktivität: Nachdem ein Ereignis in einer Graininstanz ausgelöst wurde, aktualisiert der Konsistenzanbieter nicht nur den Speicher, sondern benachrichtigt auch alle anderen Graininstanzen.

Allgemeine Informationen zum Konsistenzmodell finden Sie im TechReport und GSP-Paper (Global Sequence Protocol).

Bedingte Ereignisse

Race-Ereignisse können problematisch sein, wenn sie in Konflikt miteinander stehen, also aus irgendeinem Grund nicht beide committet werden sollten. Wenn Sie beispielsweise Geld von einem Bankkonto abheben, können zwei Instanzen unabhängig voneinander feststellen, dass genügend Mittel für eine Auszahlung vorhanden sind, und ein Auszahlungsereignis auslösen. Die Kombination der beiden Ereignisse könnte jedoch zu einer Überziehung führen. Um dies zu vermeiden, unterstützt die JournaledGrain-API eine RaiseConditionalEvent-Methode.

bool success = await RaiseConditionalEvent(
    new WithdrawalEvent() { /* ... */ });

Bedingte Ereignisse überprüfen doppelt, ob die lokale Version mit der Version im Speicher übereinstimmt. Wenn nicht, bedeutet dies, dass die Ereignissequenz in der Zwischenzeit gewachsen ist, was bedeutet, dass dieses Ereignis eine Racebedingung gegen ein anderes Ereignis verloren hat. In diesem Fall wird das bedingte Ereignis nicht an das Protokoll angehängt, und RaiseConditionalEvent gibt „False“ zurück.

Dies entspricht der Verwendung von E-Tags bei bedingten Speicheraktualisierungen und bietet ebenfalls einen einfachen Mechanismus zur Vermeidung von Konflikten beim Committen von Ereignissen.

Es ist möglich und sinnvoll, für ein und dasselbe Grain sowohl bedingte als auch unbedingte Ereignisse zu verwenden, z. B. ein DepositEvent und ein WithdrawalEvent. Einzahlungen müssen nicht bedingt sein: Selbst wenn ein DepositEvent eine Racebedingung verliert, muss es nicht abgebrochen werden, kann aber trotzdem an die globale Ereignissequenz angefügt werden.

Das Warten auf die von RaiseConditionalEvent zurückgegebene Aufgabe reicht aus, um das Ereignis zu bestätigen, es ist also nicht erforderlich, auch ConfirmEvents aufzurufen.

Explizite Synchronisierung

Manchmal sollten Sie sicherstellen, dass ein Getreide vollständig mit der neuesten Version übereinstimmt. Dies kann erzwungen werden, indem Sie Folgendes aufrufen:

await RefreshNow();

Dies bewirkt zweierlei:

  1. Alle nicht bestätigten Ereignisse werden bestätigt.
  2. Die neueste Version wird aus dem Speicher geladen.