Glueジョブのブックマークの挙動を確認してみた

# 背景・目的
Glueのジョブブックマークについて、簡単に利用したことはありましたが詳細を調べたことはありませんでした。あらためて整理し簡単に動作確認をしてみます。

# まとめ
Glueのジョブブックマークは、下記の特徴があります。
- 目的は、新しいデータのみ処理する（古いデータを処理しない）ための仕組み。
- 対応可能なデータソースは、S3とJDBC。

# 概要
## ブックマークの概要
[こちらのAWSのドキュメント](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/monitor-continuations.html)を元に整理します。
- 古いデータを処理しない。新しいデータを処理するための仕組みです。
- ジョブのブックマークが対応可能なデータソースは下記のとおりです。
    - S3
    - JDBC

### S3のジョブブックマークについて
Glueのバージョンにより、対応可能なソース形式（ファイルフォーマット）が異なります。バージョン1.0以上であれば下記が対応可能です。バージョン0.9以下をご利用の場合は、カラムナーフォーマット（Parquet、ORC）に対応されていないようです。
- JSON
- CSV
- Apache Avro
- XML
- Parquet
- ORC

### JDBCのジョブブックマークについて
::: note warn
ブックマークは、更新された行に対しては機能しない。（仕組みからして当然だが、あらためて認識しておく。）
:::
JDBCについては、テーブルごとに 1 つ以上の列をブックマークキーとして使用して、新しいデータおよび処理済みのデータを決定します。
- ブックマークに指定するキー（ブックマークキー）は、結合して単一の複合キーを形成する
- ブックマークキーには任意の列を指定が可能
- **AWS Glue は、ジョブブックマークキーとして大文字と小文字を区別する列の使用をサポートしていない。**
- ユーザ定義のブックマークキー使用有無による違いは下記の通りです。

| |指定あり |指定なし |
|:-- |:-- |:-- |
|ブックマークキー |ユーザが指定したキー |プライマリキー |
|ギャップ |キー値は一定間隔で増減され、ギャップ許容される | プライマリキーが連続してギャップなく増減することは許容されます。|

## Glue でジョブブックマークを使用する
設定のためのオプションは、3つあります。
- 有効
    - 処理されたデータを追跡し、前回のチェックポイント移行を処理する。
- 無効
    - 全件処理する。ユーザがコントロールする際にはこちらを選択する。
- 一時停止
    - 前回実行した際のチェックポイントからの増分のみ処理。または下記の2つのオプションを指定して過去のFROM〜TOの実行IDを指定して実行します。なお、このパラメータは**2つ同時に指定する**ことが必須になります。
        - job-bookmark-from <from-value>
        - job-bookmark-to <to-value>

### S3 入力ソースの場合
- オブジェクトの最終更新日時を確認して、どのオブジェクトを再処理する必要があるのかを確認します。
- 入力ソースデータが最後のジョブ実行以降に変更されている場合、ジョブを再度実行すると、ファイルが再度処理されます。

### リセットと巻き戻し
::: note warn
リセットと巻き戻しではターゲットファイルは、追跡してくれないので、リランによる冪等性が担保される仕組みを実装しておくことが必要。
:::
- Glue Sparkではジョブの巻き戻しをサポートしている。これにより、データの過去のバックフィルシナリオに対応できます。
- すべてのデータを再処理する場合は、ジョブのブックマークをリセットします。CLIの場合、下記のように指定する。
    ```aws glue reset-job-bookmark --job-name my-job-name```

### 変換コンテキスト
::: note warn
transformation_ctx を指定しない場合は、ブックマークを有効にしても機能しないので注意が必要。
:::
GlueのDynamicFrameのメソッドには、`transformation_ctx`というパラメータが含まれています。（オプション）　下記に詳細を記載します。
- ジョブのブックマークの状態を識別するために使用されるとのこと。ブックマーク状態を保存するキーにインデックスを付ける。

### AWS Glue の生成したスクリプトでジョブのブックマークを使用する
::: note info
多量のファイルが有る場合、OOMの懸念があるため、`useS3ListImplementation:True`にすることで、軽減可能。使用例は下記の通り。
```
# from_catalogの場合
datasource0 = glueContext.create_dynamic_frame.from_catalog(database = "database", table_name = "table", additional_options = {'useS3ListImplementation': True}, transformation_ctx = "datasource0")

# from_optionsの場合
datasource0 = glueContext.create_dynamic_frame.from_options(connection_type="s3", connection_options = {"paths": ["s3://input_path"], "useS3ListImplementation":True,"recurse":True}, format="json")
```
:::

- ジョブのブックマークはジョブの状態を保存します。状態の各インスタンスは、ジョブ名とバージョン番号がキーになっている。
- スクリプトの先頭に job.init() と、末尾に job.commit() を常に記述することで機能する。
    - ブックマークサービスを初期化
    - サービスの状態変化を更新するために使用
- 要素は、スクリプト内のソース、変換、シンクインスタンスごとに固有であり、1 つの状態の中に複数の状態要素が存在する。
- 状態要素はユーザースクリプトから job.commit が呼び出されたときにアトミックに保存します。

# 実践
## S3がデータソースの場合
### 事前準備
1. インプット用とアウトプット用の2つのバケットを用意します。
1. インプット用のバケットにファイルをアップロードします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/0436b27a-9e91-05c4-1593-56cd8e55dfdb.png)

### Glueジョブを作成
1. 下記のようにインプットとアウトプットだけのシンプルなジョブを作成します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/fbbdce86-a2b6-0870-9add-a28cebcb0eba.png)
1. インプットのスキーマは、idとvalueだけのシンプルなものです。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/0456626b-6ad1-e0a4-a630-86dabbfe5630.png)
1. アウトプットのパーティションキーに「id」を指定します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/1cfd42c7-bb08-efd6-c5a4-18dce5d3e99c.png)
1. 自動生成されたスクリプトに、「transformation_ctx」が設定されていることを確認します。
    ```
    
    job.init(args["JOB_NAME"], args)
    
    # Script generated for node S3 bucket
    S3bucket_node1 = glueContext.create_dynamic_frame.from_options(
        format_options={"multiline": False},
        connection_type="s3",
        format="json",
        connection_options={"paths": ["s3://{S3バケット名}"], "recurse": True},
        transformation_ctx="S3bucket_node1",
    )
    
    # Script generated for node ApplyMapping
    ApplyMapping_node2 = ApplyMapping.apply(
        frame=S3bucket_node1,
        mappings=[("id", "int", "id", "int"), ("value", "int", "value", "int")],
        transformation_ctx="ApplyMapping_node2",
    )
    
    # Script generated for node S3 bucket
    S3bucket_node3 = glueContext.write_dynamic_frame.from_options(
        frame=ApplyMapping_node2,
        connection_type="s3",
        format="json",
        connection_options={
            "path": "s3://{S3バケット名}/bookmark/",
            "partitionKeys": ["id"],
        },
        transformation_ctx="S3bucket_node3",
    )
    
    job.commit()
    ```


1. ブックマークのデフォルトは「disable」なので、「Enable」への変更が必要です。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b0bd1b99-0b16-ae26-48a8-dafab821dce0.png)

### 初回実行（ジョブを初めて実行）
1. インプットのデータは下記のとおりです。(これがS3に配置されています。)
    ```
    $ cat file1.json 
    {"id":1,"value":1}
    {"id":2,"value":2}
    {"id":3,"value":3}
    {"id":4,"value":4}
    {"id":5,"value":5}
    {"id":6,"value":6}
    {"id":7,"value":7}
    {"id":8,"value":8}
    {"id":9,"value":9}
    {"id":10,"value":10}%                                                                                                                    $
    ``` 

1. ジョブのブックマークの状態を事前に確認します。実行前はエラーになるようです。
    ```
    $ aws glue get-job-bookmark --job-name bookmark-test-for-s3
    
    An error occurred (EntityNotFoundException) when calling the GetJobBookmark operation: Continuation for job bookmark-test-for-s3 not found
    $
    ```

1. ジョブを実行し正常終了を確認します。
1. ファイルができました。想定通り、IDをキーにしたパーティション毎にファイルができていました。
    ```
    $ aws s3 ls {s3バケット名}/bookmark/ --recursive
    2023-03-18 19:43:43         11 bookmark/id=1/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         12 bookmark/id=10/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         11 bookmark/id=2/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         11 bookmark/id=3/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         11 bookmark/id=4/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         11 bookmark/id=5/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         11 bookmark/id=6/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         11 bookmark/id=7/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         11 bookmark/id=8/run-S3bucket_node3-1-part-r-00000
    2023-03-18 19:43:44         11 bookmark/id=9/run-S3bucket_node3-1-part-r-00000
    $
    ```

1. １件だけファイルの中身を確認します。想定どおりです。
    ```
    $ aws s3 cp s3://{s3バケット名}/bookmark/id=1/run-S3bucket_node3-1-part-r-00000 - | cat
    {"value":1}
    $                                                                              
    ```

1. ブックマークの状態を確認します。レスポンスが返されました。
    ```
    $ aws glue get-job-bookmark --job-name bookmark-test-for-s3
    {
        "JobBookmarkEntry": {
            "JobName": "bookmark-test-for-s3",
            "Version": 2,
            "Run": 2,
            "Attempt": 0,
            "RunId": "jr_6d8cd868f0791c1793d45f95261c2144d9825f58da3d451d831d722f82ed26a8",
            "JobBookmark": "{\"S3bucket_node1\":{\"jsonClass\":\"HadoopDataSourceJobBookmarkState\",\"timestamps\":{\"RUN\":\"1\",\"HIGH_BAND\":\"900000\",\"CURR_LATEST_PARTITION\":\"0\",\"CURR_LATEST_PARTITIONS\":\"\",\"CURR_RUN_START_TIME\":\"2023-03-18T10:43:24.612Z\",\"INCLUDE_LIST\":\"\"}}}"
        }
    }
    $ 
    ```

### ファイルはそのままでジョブをリラン
1. 確認しやすいようにアウトプットのファイルを削除しておきます。下記から消えていることがわかります。
    ```
    $ aws s3 ls {s3バケット名}/bookmark/ --recursive
    $
    ```

1. ジョブを再実行し、正常終了を確認します。2回目の実行結果（20:02）が確認できます。
    ```
    $  aws glue get-job-runs --job-name bookmark-test-for-s3
    {
        "JobRuns": [
            {
                "Id": "jr_fdb512ad8b8a0e412f75f1a341e6ba41fd5f830aa362a56802be0f2839035213",
                "Attempt": 0,
                "JobName": "bookmark-test-for-s3",
                "StartedOn": "2023-03-18T20:02:58.589000+09:00",
                "LastModifiedOn": "2023-03-18T20:03:58.786000+09:00",
                "CompletedOn": "2023-03-18T20:03:58.786000+09:00",
                "JobRunState": "SUCCEEDED",
                "PredecessorRuns": [],
                "AllocatedCapacity": 2,
                "ExecutionTime": 33,
                "Timeout": 10,
                "MaxCapacity": 2.0,
                "WorkerType": "G.1X",
                "NumberOfWorkers": 2,
                "LogGroupName": "/aws-glue/jobs",
                "GlueVersion": "4.0"
            },
            {
                "Id": "jr_6d8cd868f0791c1793d45f95261c2144d9825f58da3d451d831d722f82ed26a8",
                "Attempt": 0,
                "JobName": "bookmark-test-for-s3",
                "StartedOn": "2023-03-18T19:42:57.217000+09:00",
                "LastModifiedOn": "2023-03-18T19:43:54.763000+09:00",
                "CompletedOn": "2023-03-18T19:43:54.763000+09:00",
                "JobRunState": "SUCCEEDED",
                "PredecessorRuns": [],
                "AllocatedCapacity": 2,
                "ExecutionTime": 50,
                "Timeout": 10,
                "MaxCapacity": 2.0,
                "WorkerType": "G.1X",
                "NumberOfWorkers": 2,
                "LogGroupName": "/aws-glue/jobs",
                "GlueVersion": "4.0"
            }
        ]
    }
    $ 
    ```

1. ファイル出力を確認します。 想定通り出力されません。
    ```
    $ aws s3 ls {S3バケット名}/bookmark/ --recursive 
    $
    ```

1. ブックマークの状態を確認します。Version、Runが繰り上がっています。またPreviousRunIdが追加になったようです。
    ```
    $ aws glue get-job-bookmark --job-name bookmark-test-for-s3
    {
        "JobBookmarkEntry": {
            "JobName": "bookmark-test-for-s3",
            "Version": 4,
            "Run": 3,
            "Attempt": 0,
            "PreviousRunId": "jr_6d8cd868f0791c1793d45f95261c2144d9825f58da3d451d831d722f82ed26a8",
            "RunId": "jr_fdb512ad8b8a0e412f75f1a341e6ba41fd5f830aa362a56802be0f2839035213",
            "JobBookmark": "{\"S3bucket_node1\":{\"jsonClass\":\"HadoopDataSourceJobBookmarkState\",\"timestamps\":{\"RUN\":\"2\",\"HIGH_BAND\":\"900000\",\"CURR_LATEST_PARTITION\":\"0\",\"CURR_LATEST_PARTITIONS\":\"\",\"CURR_RUN_START_TIME\":\"2023-03-18T11:03:44.560Z\",\"INCLUDE_LIST\":\"\"}}}"
        }
    }
    $
    ```

### ブックマークのリセット
1. ブックマークのリセット機能を試します。下記のコマンドを実行しリセットします。
    ```
    $ aws glue reset-job-bookmark --job-name bookmark-test-for-s3
    {
        "JobBookmarkEntry": {
            "JobName": "bookmark-test-for-s3",
            "Version": 5,
            "Run": 4,
            "Attempt": 0,
            "JobBookmark": ""
        }
    }
    $
    ```
1. ブックマークの状態を確認します。リセットしたときと同様のレスポンスになりました。
    ```
    $ aws glue get-job-bookmark --job-name bookmark-test-for-s3  
    {
        "JobBookmarkEntry": {
            "JobName": "bookmark-test-for-s3",
            "Version": 5,
            "Run": 4,
            "Attempt": 0
        }
    }
    $
    ```
1. ジョブをリランします。3回目の実行結果（20:16）が確認できました。
    ```
    $ aws glue get-job-runs --job-name bookmark-test-for-s3     
    {
        "JobRuns": [
            {
                "Id": "jr_0b64313da1cead77c4a26659a91568729eaaf76e17d716dacdc52e715ddfe4ad",
                "Attempt": 0,
                "JobName": "bookmark-test-for-s3",
                "StartedOn": "2023-03-18T20:16:35.844000+09:00",
                "LastModifiedOn": "2023-03-18T20:17:35.097000+09:00",
                "CompletedOn": "2023-03-18T20:17:35.097000+09:00",
                "JobRunState": "SUCCEEDED",
                "PredecessorRuns": [],
                "AllocatedCapacity": 2,
                "ExecutionTime": 52,
                "Timeout": 10,
                "MaxCapacity": 2.0,
                "WorkerType": "G.1X",
                "NumberOfWorkers": 2,
                "LogGroupName": "/aws-glue/jobs",
                "GlueVersion": "4.0"
            },
            {
            ・・・・
    ```

1. S3の結果を確認します。リセット後、想定通り出力されました。
    ```
    $ aws s3 ls {s3バケット名}/bookmark/ --recursive
    2023-03-18 20:17:24         11 bookmark/id=1/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         12 bookmark/id=10/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         11 bookmark/id=2/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         11 bookmark/id=3/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         11 bookmark/id=4/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         11 bookmark/id=5/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         11 bookmark/id=6/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         11 bookmark/id=7/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         11 bookmark/id=8/run-S3bucket_node3-4-part-r-00000
    2023-03-18 20:17:24         11 bookmark/id=9/run-S3bucket_node3-4-part-r-00000
    $
    ```

1. ブックマークの状態を確認します。Versionが7、Runが7になり、JobBookmarkにも値が追加されました。
```
$ aws glue get-job-bookmark --job-name bookmark-test-for-s3  
{
    "JobBookmarkEntry": {
        "JobName": "bookmark-test-for-s3",
        "Version": 7,
        "Run": 5,
        "Attempt": 0,
        "RunId": "jr_0b64313da1cead77c4a26659a91568729eaaf76e17d716dacdc52e715ddfe4ad",
        "JobBookmark": "{\"S3bucket_node1\":{\"jsonClass\":\"HadoopDataSourceJobBookmarkState\",\"timestamps\":{\"RUN\":\"4\",\"HIGH_BAND\":\"900000\",\"CURR_LATEST_PARTITION\":\"0\",\"CURR_LATEST_PARTITIONS\":\"\",\"CURR_RUN_START_TIME\":\"2023-03-18T11:17:02.388Z\",\"INCLUDE_LIST\":\"\"}}}"
    }
}
$
```

### ファイルを追加し実行
1. ファイルを新たに追加します。追加するファイルは下記の内容になります。
    ```
    $ cat file2.json 
    {"id":11,"value":11}
    {"id":12,"value":12}
    {"id":13,"value":13}
    {"id":14,"value":14}
    {"id":15,"value":15}
    {"id":16,"value":16}
    {"id":17,"value":17}
    {"id":18,"value":18}
    {"id":19,"value":19}
    {"id":20,"value":20}
    $
    ```

1. S3にアップロードします。インプットに、file1とfile2がアップロードされたことがわかります。
    ```
    $ aws s3 cp file2.json s3://{S3バケット名}/20230318/20/20/
    upload: ./file2.json to s3://{S3バケット名}/20230318/20/20/file2.json
    $ aws s3 ls s3://{S3バケット名}/20230318/ --recursive  
    2023-03-18 19:20:56          0 20230318/
    2023-03-18 19:21:03          0 20230318/19/
    2023-03-18 19:21:11          0 20230318/19/20/
    2023-03-18 19:21:19        191 20230318/19/20/file1.json
    2023-03-18 20:27:39          0 20230318/20/
    2023-03-18 20:27:45          0 20230318/20/20/
    2023-03-18 20:30:35        209 20230318/20/20/file2.json
    $
    ```

1. 確認しやすいようにアウトプットのファイルを削除しておきます。下記から消えていることがわかります。
    ```
    $ aws s3 ls {s3バケット名}/bookmark/ --recursive
    $
    ```

1. ジョブをリランします。4回目の実行結果（20:32）が確認できました。
    ```
    $ aws glue get-job-runs --job-name bookmark-test-for-s3      
    {
        "JobRuns": [
            {
                "Id": "jr_76da6c75284dd951647193a0a7ff617b95e52a35c8db6605ba0581222f97e4fc",
                "Attempt": 0,
                "JobName": "bookmark-test-for-s3",
                "StartedOn": "2023-03-18T20:32:53.364000+09:00",
                "LastModifiedOn": "2023-03-18T20:33:51.117000+09:00",
                "CompletedOn": "2023-03-18T20:33:51.117000+09:00",
                "JobRunState": "SUCCEEDED",
                "PredecessorRuns": [],
                "AllocatedCapacity": 2,
                "ExecutionTime": 51,
                "Timeout": 10,
                "MaxCapacity": 2.0,
                "WorkerType": "G.1X",
                "NumberOfWorkers": 2,
                "LogGroupName": "/aws-glue/jobs",
                "GlueVersion": "4.0"
            },
            {
            ・・・・
    ```
1. S3の結果を確認します。想定通り、新たに追加したファイルのみ出力されました。
    ```
    $ aws s3 ls s3://{S3バケット名}/ --recursive 
    2023-03-18 20:33:40         12 bookmark/id=11/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=12/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=13/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=14/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=15/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=16/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=17/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=18/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=19/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=20/run-S3bucket_node3-5-part-r-00000
    $
    ```
1. ブックマークの状態を確認します。Versionが9、Runが6になりました。CURR_RUN_START_TIMEのタイムスタンプも更新されています。
    ```
    $ aws glue get-job-bookmark --job-name bookmark-test-for-s3  
    {
        "JobBookmarkEntry": {
            "JobName": "bookmark-test-for-s3",
            "Version": 9,
            "Run": 6,
            "Attempt": 0,
            "PreviousRunId": "jr_0b64313da1cead77c4a26659a91568729eaaf76e17d716dacdc52e715ddfe4ad",
            "RunId": "jr_76da6c75284dd951647193a0a7ff617b95e52a35c8db6605ba0581222f97e4fc",
            "JobBookmark": "{\"S3bucket_node1\":{\"jsonClass\":\"HadoopDataSourceJobBookmarkState\",\"timestamps\":{\"RUN\":\"5\",\"HIGH_BAND\":\"900000\",\"CURR_LATEST_PARTITION\":\"0\",\"CURR_LATEST_PARTITIONS\":\"\",\"CURR_RUN_START_TIME\":\"2023-03-18T11:33:25.455Z\",\"INCLUDE_LIST\":\"31a811a7ec94d7930ddeaee57f0d393e,2ec954039bc8fcef1530d23d228cc898\"}}}"
        }
    }
    $
    ```

### ファイルを更新し実行
今までのファイルを上書きし対象になるか確認します。
1. 事前にアウトプットファイルを確認します。id=11〜20のファイルができています。
    ```
    $ aws s3 ls s3://{s3バケット名}/ --recursive     
    2023-03-18 20:33:40         12 bookmark/id=11/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=12/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=13/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=14/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=15/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=16/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=17/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=18/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=19/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=20/run-S3bucket_node3-5-part-r-00000
    $
    ```

1. アップロード前のインプットファイルを確認します。file1.jsonは、19:21のタイムスタンプです。これを置き換えます。
    ```
    $ aws s3 ls s3://{s3バケット名}/20230318/ --recursive
    2023-03-18 19:20:56          0 20230318/
    2023-03-18 19:21:03          0 20230318/19/
    2023-03-18 19:21:11          0 20230318/19/20/
    2023-03-18 19:21:19        191 20230318/19/20/file1.json
    2023-03-18 20:27:39          0 20230318/20/
    2023-03-18 20:27:45          0 20230318/20/20/
    2023-03-18 20:30:35        209 20230318/20/20/file2.json
    $
    ```

1. ファイルをアップロードします。file1.jsonのタイムスタンプが21:42に変わったことがわかります。
    ```
    $ aws s3 cp file1.json s3://{s3バケット名}/20230318/19/20/ 
    
    upload: ./file1.json to s3://{s3バケット名}/20230318/19/20/file1.json
    $ aws s3 ls s3://{s3バケット名}/20230318/ --recursive     
    2023-03-18 19:20:56          0 20230318/
    2023-03-18 19:21:03          0 20230318/19/
    2023-03-18 19:21:11          0 20230318/19/20/
    2023-03-18 21:42:16        191 20230318/19/20/file1.json
    2023-03-18 20:27:39          0 20230318/20/
    2023-03-18 20:27:45          0 20230318/20/20/
    2023-03-18 20:30:35        209 20230318/20/20/file2.json
    $
    ```

1. ジョブをリランします。5回目の実行結果（21:44）が確認できました。
    ```
    $ aws glue get-job-runs --job-name bookmark-test-for-s3         
    {
        "JobRuns": [
            {
                "Id": "jr_0d41508efc4f7f5097088d1f14acd2bd32c811b24b1dcef586f55c8de0e543fe",
                "Attempt": 0,
                "JobName": "bookmark-test-for-s3",
                "StartedOn": "2023-03-18T21:43:57.211000+09:00",
                "LastModifiedOn": "2023-03-18T21:44:44.512000+09:00",
                "CompletedOn": "2023-03-18T21:44:44.512000+09:00",
                "JobRunState": "SUCCEEDED",
                "PredecessorRuns": [],
                "AllocatedCapacity": 2,
                "ExecutionTime": 40,
                "Timeout": 10,
                "MaxCapacity": 2.0,
                "WorkerType": "G.1X",
                "NumberOfWorkers": 2,
                "LogGroupName": "/aws-glue/jobs",
                "GlueVersion": "4.0"
            },
            {
    ・・・・
    ```
1. S3の結果を確認します。想定通り、id=1〜10のファイルが追加されました。（id=11〜20)のタイムスタンプは20:33のままです。
    ```
    $ aws s3 ls s3://{s3バケット名}/ --recursive              
    2023-03-18 21:44:33         11 bookmark/id=1/run-S3bucket_node3-6-part-r-00000
    2023-03-18 21:44:34         12 bookmark/id=10/run-S3bucket_node3-6-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=11/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=12/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=13/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=14/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=15/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=16/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=17/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=18/run-S3bucket_node3-5-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=19/run-S3bucket_node3-5-part-r-00000
    2023-03-18 21:44:34         11 bookmark/id=2/run-S3bucket_node3-6-part-r-00000
    2023-03-18 20:33:40         12 bookmark/id=20/run-S3bucket_node3-5-part-r-00000
    2023-03-18 21:44:34         11 bookmark/id=3/run-S3bucket_node3-6-part-r-00000
    2023-03-18 21:44:34         11 bookmark/id=4/run-S3bucket_node3-6-part-r-00000
    2023-03-18 21:44:34         11 bookmark/id=5/run-S3bucket_node3-6-part-r-00000
    2023-03-18 21:44:34         11 bookmark/id=6/run-S3bucket_node3-6-part-r-00000
    2023-03-18 21:44:34         11 bookmark/id=7/run-S3bucket_node3-6-part-r-00000
    2023-03-18 21:44:34         11 bookmark/id=8/run-S3bucket_node3-6-part-r-00000
    2023-03-18 21:44:34         11 bookmark/id=9/run-S3bucket_node3-6-part-r-00000
    $
    ```



# 考察
今回は、Glueのジョブブックマークを試してみました。今更ですがあらためて手を動かして一つ一つ確認することでより具体的にイメージができました。

# 参考
https://docs.aws.amazon.com/ja_jp/glue/latest/dg/monitor-continuations.html#monitor-continuations-implement