マップはローダーに関連付ける ことができます。ローダーは、オブジェクトがマップに見つからない場合 (キャッシュ・ミスの場合) に、そのオブジェクトをフェッチするためにも、またトランザクションのコミット時に変更をバックエンドに書き込むためにも使用されます。 ローダーは、マップへのデータのプリロードに使用することもできます。Loader インターフェースの preloadMap メソッドは、マップ・セット内のその対応する区画がプライマリーとなると、各マップで呼び出されます。preloadMap メソッドは、レプリカでは呼び出されません。このメソッドは、提供されたセッションを使用して、対象となる参照データのすべてをバックエンドからマップにロードしようとします。関係するマップは、preloadMap メソッドに渡される BackingMap 引数によって識別されます。
void preloadMap(Session session, BackingMap backingMap) throws LoaderException;
区画に分割されたマップ・セットでのプリロード
マップは、N 個の区画に分割することができます。 したがってマップは、複数のサーバーに渡ってストライプすることができます。この場合、各エントリーは、これらのサーバーのうちの 1 つにのみ保管されているキーによって識別されます。アプリケーションは、マップのすべてのエントリーを保持するに際して単一の Java 仮想マシン (JVM) のヒープ・サイズによる制限を受けなくなったため、非常に大きいマップをデータ・グリッドに保持することができます。 Loader インターフェースの preloadMap メソッドがプリロードされるアプリケーションは、それがプリロードするデータのサブセットを識別する必要があります。常に、固定数の区画が存在します。 この数を判別するには、以下のコード例を使用してください。
int numPartitions = backingMap.getPartitionManager().getNumOfPartitions();
int myPartition = backingMap.getPartitionId();
このコード例は、データベースからプリロードするデータのサブセットを、アプリケーションがどのように識別できるかを示しています。アプリケーションは、マップが最初に区画に分割されていない場合でも、
これらのメソッドを常に使用しなければなりません。これらのメソッドによって柔軟性が実現されます。管理者が後でマップを区画に分割した場合でも、ローダーは正常に機能し続けます。アプリケーションは、バックエンドから myPartition サブセットを検索する照会を発行する必要があります。テーブル内のデータを簡単に区画に分割できるなんらかの自然な照会がある場合を除き、データベースが使用される場合は、所定レコードの区画 ID の列を持つ方が、処理が容易である可能性があります。
複製されたデータ・グリッド用のローダーを実装する方法の例については、レプリカ・プリロード・コントローラーを使用したローダーの作成を参照してください。
パフォーマンス
プリロードの実装では、複数のオブジェクトを単一トランザクションでマップに保管して、データをバックエンドからマップにコピーします。トランザクションごとに保管されるレコードの最適数は、複雑さやサイズなど、いくつかの要因によって決まります。 例えば、トランザクションに 100 エントリーを超えるブロックが含まれると、以後は、エントリーの数を増やすに従ってパフォーマンス利益が減少していきます。 最適数を知るためには、まず 100 エントリーから始めて、徐々に数を増やしていきます。これをパフォーマンス利益がゼロに減少するまで続けます。 トランザクションが大きいほど、レプリカ生成パフォーマンスが向上します。 ただし、プライマリーのみがプリロード・コードを実行することに注意してください。 プリロードされたデータは、プライマリーから、オンラインになっているすべてのレプリカに複製されます。
マップ・セットのプリロード
アプリケーションが複数のマップを持つマップ・セットを使用する場合、各マップはそれぞれ独自のローダーを持ちます。各ローダーに、プリロード・メソッドがあります。各マップはデータ・グリッドによって順次にロードされます。 1 つのマップをプリロード・マップに指定して全マップをプリロードすると、より効率的になる可能性があります。このプロセスは、アプリケーション規則です。例えば、部門と従業員という 2 つのマップが、部門マップと従業員マップの両方をプリロードするために、部門 Loader を使用するとします。このプロシージャーにより、トランザクション上、アプリケーションで部門が必要な場合、その部門の従業員がキャッシュされます。部門 Loader が部門をバックエンドからプリロードするときに、その部門の従業員もフェッチします。その後で、部門オブジェクトとそれに関連する従業員オブジェクトが、単一のトランザクションを使用して、マップに追加されます。
リカバリー可能なプリロード
非常に大きいデータ・セットをキャッシュする必要がある場合があります。このデータのプリロードは、非常に時間がかかる可能性があります。 アプリケーションがオンラインになる前に、プリロードを完了しなければならない場合もあります。 プリロードをリカバリー可能にすると、便利です。100 万個のレコードをプリロードする必要があるとします。プライマリーがこれらのレコードをプリロードし、 800,000 件目のレコードの時点でプライマリーが失敗するとします。通常、新規プライマリーとして選択されたレプリカは、複製状態をクリアして、最初からプリロードを開始します。eXtreme Scale では、ReplicaPreloadController インターフェースを使用できます。アプリケーションのローダーで、ReplicaPreloadController インターフェースを実装する必要が生じることもあります。この例では、単一メソッド Status checkPreloadStatus(Session session, BackingMap bmap); をローダーに追加します。Loader インターフェースのプリロード・メソッドが正常に呼び出されるためには、このメソッドが eXtreme Scale ランタイムによって呼び出されます。レプリカがプライマリーにプロモートされると、常に eXtreme Scale がこのメソッド (Status) の結果をテストして、その振る舞いを決定します。
返される状況値 | eXtreme Scale の応答 |
---|---|
Status.PRELOADED_ALREADY | この状況値は、マップが完全にプリロードされていることを示しているため、eXtreme Scale はプリロード・メソッドをまったく呼び出しません。 |
Status.FULL_PRELOAD_NEEDED | eXtreme Scale はマップをクリアし、プリロード・メソッドを正常に呼び出します。 |
Status.PARTIAL_PRELOAD_NEEDED | eXtreme Scale は、マップを現状のままにして、プリロードを呼び出します。このストラテジーによって、アプリケーション・ローダーは、この時点以降プリロードを継続することができます。 |
プライマリーは、マップのプリロード中、返す必要のある状況をレプリカ側で判別できるように、複製中の MapSet 内のマップに必ず何らかの状態を残す必要があります。RecoveryMap マップなどと呼ばれる追加のマップを使用することができます。マップがプリロード中のデータで一貫して複製されるようにするため、この RecoveryMap マップは、プリロード中の同じ MapSet マップ・セットの一部である必要があります。 推奨の実装は、以下のとおりです。
プリロードがレコードの各ブロックをコミットすると、プロセスも、RecoveryMap マップ内のカウンターまたは値をそのトランザクションの一部として更新します。プリロードされたデータと RecoveryMap マップ・データは、レプリカにアトミックに複製されます。レプリカがプライマリーに格上げされると、RecoveryMap マップをチェック して何が起こったかを確認できるようになります。
RecoveryMap マップは、状態キーを持つ単一エントリーを保持できます。このキーに対するオブジェクトが存在しない場合には、完全な preload (checkPreloadStatus returns FULL_PRELOAD_NEEDED) が必要です。 この状態キーに対するオブジェクトが存在し、値が COMPLETE の場合は、プリロードが完了し、checkPreloadStatus メソッドで PRELOADED_ALREADY が返されます。 これ以外の場合、値オブジェクトは、プリロードを再開する場所を示し、checkPreloadStatus メソッドは PARTIAL_PRELOAD_NEEDED を返します。 ローダーは、プリロードが呼び出されたときにローダーに開始点がわかるように、ローダーのインスタンス変数にリカバリー・ポイントを保管できます。また、各マップが個別にプリロードされる場合、RecoveryMap マップもマップごとにエントリーを保持できます。
Loader での同期レプリカ生成モードにおけるリカバリーの処理
eXtreme Scale ランタイムは、プライマリーが失敗したときにコミット済みデータを失わないよう設計されています。次のセクションでは、使用されるアルゴリズムについて説明します。これらのアルゴリズムは、レプリカ生成グループが同期レプリカ生成を使用する場合にのみ 適用されます。ローダーはオプションです。
eXtreme Scale ランタイムは、すべての変更がプライマリーからレプリカに同期複製されるように構成することができます。 同期レプリカが配置されると、その同期レプリカは、プライマリー断片にある既存データのコピー を受け取ります。この間もプライマリーはトランザクションを受け取り続け、 受け取ったトランザクションを非同期にレプリカにコピーします。 レプリカはこの時点ではオンラインであるとは見なされません。
レプリカがプライマリーに 追いついた後、レプリカはピア・モードに入り、同期レプリカ生成 が始まります。プライマリーでコミットされたトランザクションはすべて同期レプリカに 送信され、プライマリーは各レプリカからの応答を 待ちます。ローダーを使用する、プライマリーでの同期コミット・シーケンスは、 以下の一連のステップのようになります。
Loader を使用する場合のステップ | Loader を使用しない場合のステップ |
---|---|
エントリーのロックを取得します。 | 同じ |
変更をローダーにフラッシュします。 | 操作しない |
キャッシュに変更を保存します。 | 同じ |
変更をレプリカに送信し、確認通知を待機します。 | 同じ |
TransactionCallback プラグインでローダーをコミットします。 | プラグイン・コミットが呼び出されますが、何も実行しません。 |
エントリーのロックを解除します。 | 同じ |
変更がレプリカに送信された後、ローダーにコミットされることに注意してください。変更がレプリカでコミットされる条件を判別するには、このシーケンスを訂正します。初期化時に、以下のようにプライマリーで tx リストを初期化します。
CommitedTx = {}, RolledBackTx = {}
同期コミットの処理中に、以下のシーケンスを使用します。
Loader を使用する場合のステップ | Loader を使用しない場合のステップ |
---|---|
エントリーのロックを取得します。 | 同じ |
変更をローダーにフラッシュします。 | 操作しない |
キャッシュに変更を保存します。 | 同じ |
コミット済みトランザクションで変更を送信し、トランザクションをレプリカにロールバックし、肯定応答を待機します。 | 同じ |
コミット済みトランザクションおよびロールバック済みトランザクションのリストをクリアします。 | 同じ |
TransactionCallBack プラグインでローダーをコミットします。 | TransactionCallBack プラグイン・コミットがやはり呼び出されますが、通常、何も行われません。 |
コミットが成功した場合、トランザクションがコミット済みトランザクションに追加され、成功しなかった 場合はロールバック済みトランザクションに追加されます。 | 操作しない |
エントリーのロックを解除します。 | 同じ |
レプリカ処理の場合、以下のシーケンスを使用します。
レプリカがレプリカ・モードである間は、レプリカ上でローダーによる相互作用が行われないことに注意してください。プライマリーは、すべての変更を Loader を介してプッシュする必要があります。 レプリカはデータを変更しません。 このアルゴリズムの副次作用は、レプリカに常にトランザクションがあるが、次のプライマリー・トランザクションによってこれらのトランザクションのコミット状況が送信されるまで、コミットされないことです。その場合には、トランザクションはレプリカ上でコミットまたはロールバックされます。このようになるまでは、トランザクションはコミットされません。短い時間 (数秒) 後にトランザクションの結果が送信されるようなタイマーをプライマリーに追加することができます。このタイマーは、その時刻ウィンドウに対する失効性を制限しますが、除去はしません。 こうした失効性は、レプリカ読み取りモードを使用する場合のみの問題です。それ以外の点では、失効性は、アプリケーションに影響を与えません。
プライマリーが失敗した場合、プライマリーでコミットまたはロールバックされたトランザクションがいくつかある可能性がありますが、これらの結果が含まれるメッセージがレプリカに到達しませんでした。レプリカが新規プライマリーにプロモートされる際の最初のアクションの 1 つは、この状態に対処することです。保留中の各トランザクションは、新規プライマリーのマップ・セットに対して再処理されます。ローダーがある場合は、そのローダーに各トランザクションが送られます。これらのトランザクションには、厳密な先入れ先出し法 (FIFO) 順序が適用されます。失敗したトランザクションは無視されます。例えば、3 つのトランザクション A、B、および C が保留中の場合、A はコミットし、B はロールバックし、C もコミットする可能性があります。1 つのトランザクション が他のトランザクションに影響を与えることはありません。これらのトランザクションは独立したものと見なされます。
ローダーで使用されるロジックは、フェイルオーバー・リカバリー・モードと通常モードの場合では若干異なることがあります。ローダーがフェイルオーバー・リカバリー・モードであるときは、ReplicaPreloadController インターフェースを実装することで容易に識別できます。checkPreloadStatus メソッドは、フェイルオーバー・リカバリーが完了した場合にのみ呼び出されます。 このため、Loader インターフェースの apply メソッドが checkPreloadStatus メソッドより前に呼び出される場合は、リカバリー・トランザクションになります。checkPreloadStatus メソッドが呼び出されると、フェイルオーバー・リカバリーが完了します。