

# Java 用の Lambda SnapStart のランタイムフック
<a name="snapstart-runtime-hooks-java"></a>

ランタイムフックを使用して、Lambda がスナップショットを作成する前、または Lambda がスナップショットから関数を再開した後でコードを実装できます。ランタイムフックは、オープンソースの Coordinated Restore at Checkpoint (CRaC) プロジェクトの一部として提供されています。CRaC は、[Open Java Development Kit (OpenJDK)](https://wiki.openjdk.org/display/crac) 向けに開発中です。リファレンスアプリケーションで CRaC を使用する方法の例については、GitHub にある [CRaC](https://github.com/CRaC/docs/blob/master/STEP-BY-STEP.md) リポジトリを参照してください。CRaC は、3 つの主要要素を使用します。
+ `Resource` – `beforeCheckpoint()` および `afterRestore()` の 2 つのメソッドを持つインターフェイス。これらのメソッドを使用して、スナップショット前、および復元後に実行するコードを実装します。
+ `Context <R extends Resource>` – チェックポイントと復元に関する通知を受け取るには、`Resource` が `Context` に登録されている必要があります。
+ `Core` – 静的メソッド `Core.getGlobalContext()` 経由でデフォルトのグローバル `Context` を提供するコーディネーションサービス。

`Context` および `Resource` の詳細については、CRaC ドキュメントの「[Package org.crac](https://javadoc.io/doc/io.github.crac/org-crac/latest/index.html)」を参照してください。

[org.crac package](https://github.com/CRaC/org.crac) を使用してランタイムフックを実装するには、以下の手順を実行します。Lambda ランタイムには、チェックポイント作成前と復元後にランタイムフックを呼び出す、カスタマイズされた CRaC コンテキスト実装が含まれています。

## ランタイムフックの登録と実行
<a name="runtime-hooks-registration-java"></a>

Lambda がランタイムフックを実行する順序は、登録順によって決まります。登録順序は、コード内でのインポート、定義、または実行の順序に従います。
+ `beforeCheckpoint()`: 登録の逆順で実行されます
+ `afterRestore()`: 登録順で実行されます

登録されたすべてのフックが適切にインポートされ、関数のコードに含まれている必要があります。ランタイムフックを別のファイルまたはモジュールに登録する場合は、モジュールが (直接、またはより大きなパッケージの一部として) 関数のハンドラーファイルにインポートされていることを確認する必要があります。ファイルまたはモジュールが関数ハンドラーにインポートされていない場合、Lambda はそのランタイムフックを無視します。

**注記**  
Lambda がスナップショットを作成するときは、初期化コードが最大 15 分間実行される場合があります。制限時間は 130 秒、または[設定されている関数のタイムアウト](configuration-timeout.md) (最大 900 秒) のいずれか長い方です。`beforeCheckpoint()` ランタイムフックは初期化コードの時間制限にカウントされます。Lambda がスナップショットを復元するときは、タイムアウト制限 (10 秒) 内に、ランタイムがロードされて `afterRestore()` ランタイムフックが完了する必要があります。その時間を超えると、SnapStartTimeoutException が発生します。

## ステップ 1: ビルド設定を更新する
<a name="runtime-hooks-java-update-build"></a>

ビルド設定に `org.crac` 依存関係を追加します。以下の例は、Gradle を使用しています。他のビルドシステムの例については、[Apache Maven ドキュメント](https://search.maven.org/artifact/io.github.crac/org-crac/0.1.3/jar)を参照してください。

```
dependencies {
    compile group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.1'
    # All other project dependecies go here:
    # ...
    # Then, add the org.crac dependency:
 implementation group: 'org.crac', name: 'crac', version: '1.4.0'
}
```

## ステップ 2: Lambda ハンドラーを更新する
<a name="runtime-hooks-java-update-handler"></a>

Lambda 関数ハンドラーは、イベントを処理する関数コード内のメソッドです。関数が呼び出されると、Lambda はハンドラーメソッドを実行します。関数は、ハンドラーが応答を返すか、終了するか、タイムアウトするまで実行されます。

詳細については、「[Java の Lambda 関数ハンドラーの定義](java-handler.md)」を参照してください。

以下のハンドラー例は、チェックポイント作成前 (`beforeCheckpoint()`) と復元後 (`afterRestore()`) にコードを実行する方法を示しています。このハンドラーは、ランタイムが管理するグローバル `Context` への `Resource` の登録も行います。

**注記**  
Lambda がスナップショットを作成するときは、初期化コードが最大 15 分間実行される場合があります。制限時間は 130 秒、または[設定されている関数のタイムアウト](configuration-timeout.md) (最大 900 秒) のいずれか長い方です。`beforeCheckpoint()` ランタイムフックは初期化コードの時間制限にカウントされます。Lambda がスナップショットを復元するときは、タイムアウト制限 (10 秒) 内にランタイム (JVM) がロードされ、`afterRestore()` ランタイムフックが完了される必要があります。その時間を超えると、SnapStartTimeoutException が発生します。

```
...
  import org.crac.Resource;
  import org.crac.Core;
  ... 
public class CRaCDemo implements RequestStreamHandler, Resource {
    public CRaCDemo() {
      Core.getGlobalContext().register(this);
    }
    public String handleRequest(String name, Context context) throws IOException {
      System.out.println("Handler execution");
      return "Hello " + name;
    }
    @Override
    public void beforeCheckpoint(org.crac.Context<? extends Resource> context)
        throws Exception {
      System.out.println("Before checkpoint");
    }
    @Override
    public void afterRestore(org.crac.Context<? extends Resource> context)
        throws Exception {
      System.out.println("After restore");
```

`Context` は、登録されたオブジェクトへの [https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ref/WeakReference.html](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ref/WeakReference.html) のみを維持します。[https://javadoc.io/static/io.github.crac/org-crac/0.1.3/org/crac/Resource.html](https://javadoc.io/static/io.github.crac/org-crac/0.1.3/org/crac/Resource.html) に対してガベージコレクションが行われた場合、ランタイムフックは実行されません。ランタイムフックが実行されることを保証するには、コードが `Resource` への強参照を維持する必要があります。

以下は、避ける必要があるパターンの 2 つの例です。

**Example – 強参照がないオブジェクト**  

```
Core.getGlobalContext().register( new MyResource() );
```

**Example – 匿名クラスのオブジェクト**  

```
Core.getGlobalContext().register( new Resource() {
   
   @Override
   public void afterRestore(Context<? extends Resource> context) throws Exception {
    // ...
   }
   
   @Override
   public void beforeCheckpoint(Context<? extends Resource> context) throws Exception {
    // ...
   }

} );
```

これらの代わりに、強参照を維持します。以下の例では、登録されたリソースに対してガベージコレクションが行われず、ランタイムフックが一貫的に実行されます。

**Example – 強参照を持つオブジェクト**  

```
Resource myResource = new MyResource(); // This reference must be maintained to prevent the registered resource from being garbage collected
Core.getGlobalContext().register( myResource );
```