

# Lambda 関数スナップショットの前後のコード実装
<a name="snapstart-runtime-hooks"></a>

ランタイムフックを使用して、Lambda がスナップショットを作成する前、または Lambda がスナップショットから関数を再開した後でコードを実装できます。ランタイムフックは、以下のようにさまざまな目的に役立ちます。
+ **クリーンアップと初期化:** スナップショットを作成する前に、ランタイムフックを使用してクリーンアップオペレーションまたはリソースリリースオペレーションを実行できます。スナップショットの復元後に、ランタイムフックを使用して、スナップショットにキャプチャされなかったリソースまたは状態を再初期化できます。
+ **動的設定:** ランタイムフックを使用して、スナップショットの作成前または復元後に、設定やその他のメタデータを動的に更新できます。これは、関数がランタイム環境の変化に適応する必要がある場合に役立ちます。
+ **外部統合:** ランタイムフックを使用して、チェックポイントの作成および復元プロセスの一環として、外部サービスやシステム (通知の送信や外部状態の更新など) と統合できます。
+ **パフォーマンスチューニング:** ランタイムフックを使用して、依存関係を事前ロードするなど、関数の起動シーケンスを微調整できます。詳細については、「[パフォーマンスチューニング](snapstart-best-practices.md#snapstart-tuning)」を参照してください。

以下のページでは、任意のランタイムにランタイムフックを実装する方法について説明します。

**Topics**
+ [Java](snapstart-runtime-hooks-java.md)
+ [Python](snapstart-runtime-hooks-python.md)
+ [.NET](snapstart-runtime-hooks-dotnet.md)

# 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 );
```

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

ランタイムフックを使用して、Lambda がスナップショットを作成する前、または Lambda がスナップショットから関数を再開した後でコードを実装できます。Python ランタイムフックは、Python マネージドランタイムに含まれているオープンソースの [Snapshot Restore for Python ライブラリ](https://pypi.org/project/snapshot-restore-py/)の一部として使用できます。このライブラリには、ランタイムフックの定義に使用できる 2 つのデコレータが用意されています。
+ `@register_before_snapshot`: Lambda がスナップショットを作成する前に実行する関数の場合。
+ `@register_after_restore`: Lambda がスナップショットから関数を再開するときに実行する関数の場合。

あるいは、次の方法を使用して、ランタイムフックの呼び出し可能オブジェクトを登録することもできます。
+ `register_before_snapshot(func, *args, **kwargs)`
+ `register_after_restore(func, *args, **kwargs)`

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

Lambda がランタイムフックを実行する順序は、登録順によって決まります。
+ スナップショット前: 登録の逆順で実行されます
+ スナップショット後: 登録順で実行されます

ランタイムフックの登録の順序は、フックの定義方法によって異なります。デコレータ (`@register_before_snapshot` および `@register_after_restore`) を使用する場合、登録順序はコード内でのインポート、定義、または実行の順序に従います。登録順序をより詳細に制御する必要がある場合は、デコレータの代わりに `register_before_snapshot()` メソッドおよび `register_after_restore()` メソッドを使用します。

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

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

## 例
<a name="runtime-hooks-python-code-sample"></a>

以下のハンドラー例は、チェックポイント作成前 (`@register_before_snapshot`) と復元後 (`@register_after_restore`) にコードを実行する方法を示しています。

```
from snapshot_restore_py import register_before_snapshot, register_after_restore

def lambda_handler(event, context):
    # Handler code

@register_before_snapshot
def before_checkpoint():
    # Logic to be executed before taking snapshots

@register_after_restore
def after_restore():
    # Logic to be executed after restore
```

その他の例については、AWS GitHub リポジトリの「[Snapshot Restore for Python](https://github.com/aws/snapshot-restore-py/tree/main/examples)」を参照してください。

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

ランタイムフックを使用して、Lambda がスナップショットを作成する前、または Lambda がスナップショットから関数を再開した後でコードを実装できます。.NET ランタイムフックは、[Amazon.Lambda.Core](https://www.nuget.org/packages/Amazon.Lambda.Core) パッケージ (バージョン 2.5.0 以降) の一部として使用できます。このライブラリには、ランタイムフックの定義に使用できる 2 つの方法が用意されています。
+ `RegisterBeforeSnapshot()`: スナップショットの作成前に実行するコード
+ `RegisterAfterSnapshot()`: スナップショットからの関数の再開後に実行するコード

**注記**  
[Lambda Annotations framework for .NET](csharp-handler.md#csharp-handler-annotations) を使用している場合は、[Amazon.Lambda.Annotations](https://www.nuget.org/packages/Amazon.Lambda.Annotations) バージョン 1.6.0 以降にアップグレードして、SnapStart との互換性を確保してください。

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

初期化コードにフックを登録します。Lambda 関数の[実行モデル](csharp-handler.md#csharp-handler-setup)に基づいて、次のガイドラインを考慮します。
+ [実行可能アセンブリアプローチ](csharp-handler.md#csharp-executable-assembly-handlers)の場合は、`RunAsync` で Lambda ブートストラップを開始する前にフックを登録します。
+ [クラスライブラリアプローチ](csharp-handler.md#csharp-class-library-handlers)の場合は、ハンドラークラスコンストラクタにフックを登録します。
+ [ASP.NET Core アプリケーション](csharp-package-asp.md)の場合は、`WebApplications.Run` メソッドを呼び出す前にフックを登録します。

.NET で SnapStart 用のランタイムフックを登録するには、次の方法を使用します。

```
Amazon.Lambda.Core.SnapshotRestore.RegisterBeforeSnapshot(BeforeCheckpoint);
Amazon.Lambda.Core.SnapshotRestore.RegisterAfterRestore(AfterCheckpoint);
```

複数のフックタイプが登録されている場合、Lambda がランタイムフックを実行する順序は、登録順によって決まります。
+ `RegisterBeforeSnapshot()`: 登録の逆順で実行されます
+ `RegisterAfterSnapshot()`: 登録順で実行されます

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

## 例
<a name="runtime-hooks-dotnet-code-sample"></a>

以下の関数の例は、チェックポイント作成前 (`RegisterBeforeSnapshot`) と復元後 (`RegisterAfterRestore`) にコードを実行する方法を示しています。

```
public class SampleClass
{
    public SampleClass()
    {
        Amazon.Lambda.Core.SnapshotRestore.RegisterBeforeSnapshot(BeforeCheckpoint);
        Amazon.Lambda.Core.SnapshotRestore.RegisterAfterRestore(AfterCheckpoint);
    }

    private ValueTask BeforeCheckpoint()
    {
        // Add logic to be executed before taking the snapshot
        return ValueTask.CompletedTask;
    }

    private ValueTask AfterCheckpoint()
    {
        // Add logic to be executed after restoring the snapshot
        return ValueTask.CompletedTask;
    }

    public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
    {
        // Add business logic

        return new APIGatewayProxyResponse
        {
            StatusCode = 200
        };
    }
}
```