

# DynamoDB でのインデックスキー違反の検出と修正
<a name="GSI.OnlineOps.ViolationDetection"></a>

グローバルセカンダリインデックスの作成のバックフィルフェーズでは、Amazon DynamoDB はテーブルの各項目を調べて、インデックスに含める適格性があるかどうかを判断します。一部の項目は、インデックスキー違反の原因となるため、適格でない場合があります。このような場合、項目はテーブルに残りますが、インデックスにはその項目に対応するエントリがなくなります。

インデックスキー違反は次の場合に発生します。
+ 属性値とインデックスキーのスキーマのデータ型が一致しない場合。たとえば、`GameScores` テーブルの項目の 1 つが、`String` 型の `TopScore` 値を持っていたとします。`Number` 型の `TopScore` のパーティションキーを持つグローバルセカンダリインデックスを追加した場合、テーブルの項目はインデックスキーに違反します。
+ テーブルの属性値が、インデックスキー属性の最大長を超えている場合。パーティションキーの最大長は 2048 バイトで、ソートキーの最大長は 1024 バイトです。テーブル内の対応する属性値のいずれかがこれらの制限を超えると、テーブルの項目はインデックスキーに違反します。

**注記**  
インデックスキーとして使用される属性に String 属性または Binary 属性値が設定されている場合、属性値の長さは 0 より大きくする必要があります。そうしないと、テーブルの項目がインデックスキーに違反します。  
現時点では、このツールはこのインデックスキー違反にフラグを設定しません。

インデックスキー違反が発生した場合でも、バックフィルフェーズは中断することなく続行されます。ただし、違反している項目は、インデックスに含まれません。バックフィルフェーズ完了後は、新しいインデックスのキースキーマに違反する項目へのすべての書き込みが拒否されます。

インデックスキーに違反するテーブル内の属性値を識別して修正するには、Violation Detector ツールを使用します。Violation Detector を実行するには、スキャンするテーブルの名前、グローバルセカンダリインデックスパーティションキーとソートキーの名前とデータ型、インデックスキー違反が見つかった場合に実行するアクションを指定する設定ファイルを作成します。Violation Detector は、次の 2 つのモードのいずれかで実行できます。
+ **検出モード** – インデックスキー違反を検出します。検出モードを使用して、グローバルセカンダリインデックスでキー違反の原因となるテーブル内の項目を報告します。(必要に応じて、違反しているテーブル項目が見つかるとすぐに削除するように要求できます)。検出モードからの出力はファイルに書き込まれるので、これを使用してさらに分析できます。
+ **修正モード** – インデックスキー違反を修正します。修正モードでは、Violation Detector は検出モードからの出力ファイルと同じ形式の入力ファイルを読み込みます。修正モードでは、入力ファイルからレコードを読み込み、各レコードについて、テーブル内の対応する項目を削除または更新します。(項目の更新を選択した場合は、入力ファイルを編集して、これらの更新に適切な値を設定する必要があります)。

## Violation Detector のダウンロードおよび実行
<a name="GSI.OnlineOps.ViolationDetection.Running"></a>

Violation Detector は、実行可能な Java Archive (`.jar` ファイル) で、Windows、macOS、または Linux コンピュータ上で実行されます。Violation Detector には Java 1.7 (以降) と Apache Maven が必要です。
+ [GitHub から Violation Detector をダウンロードします](https://github.com/awslabs/dynamodb-online-index-violation-detector)

`README.md` ファイルの指示に従って、Maven を使用して Violation Detector をダウンロードしてインストールします。

Violation Detector を起動するには、`ViolationDetector.java` を構築したディレクトリで、次のコマンドを入力します。

```
java -jar ViolationDetector.jar [options]
```

Violation Detector ツールのコマンドラインでは、以下のオプションが使用できます。
+ `-h | --help` – Violation Detector の使用方法の概要とオプションを出力します。
+ `-p | --configFilePath` `value` – Violation Detector 設定ファイルの完全修飾名。詳細については、「」を参照してください[Violation Detector 設定ファイル](#GSI.OnlineOps.ViolationDetection.ConfigFile)
+ `-t | --detect` `value` – テーブル内のインデックスキー違反を検出し、Violation Detector の出力ファイルに書き込みます。このパラメータの値が `keep` に設定されている場合、キー違反のある項目は変更されません。値が `delete` に設定されている場合、キー違反のある項目はテーブルから削除されます。
+ `-c | --correct` `value` – 入力ファイルからインデックスキー違反を読み込み、テーブル内の項目に対して修正アクションを実行します。このパラメータの値が `update` に設定されている場合、キー違反のある項目は、違反していない新しい値で更新されます。値が `delete` に設定されている場合、キー違反のある項目はテーブルから削除されます。

## Violation Detector 設定ファイル
<a name="GSI.OnlineOps.ViolationDetection.ConfigFile"></a>

実行時に、Violation Detector ツールには設定ファイルが必要です。このファイルのパラメータによって、Violation Detector がアクセスできる DynamoDB リソースと、プロビジョニングされたスループットを使用できる量が決まります。以下の表は、これらのパラメータの説明です。


****  

| パラメータ名 | 説明 | 必須? | 
| --- | --- | --- | 
| `awsCredentialsFile` | AWS 認証情報を含むファイルの完全修飾名。認証情報ファイルの形式は、次のようになります。<pre>accessKey = {{access_key_id_goes_here}}<br />secretKey = {{secret_key_goes_here}} </pre> | はい | 
| `dynamoDBRegion` | テーブルが存在する AWS リージョン。例: `us-west-2`。 | はい | 
| `tableName` | スキャンされる DynamoDB テーブルの名前。 | はい | 
| `gsiHashKeyName` | インデックスパーティションキーの名前。 | はい | 
| `gsiHashKeyType` | インデックスパーティションキーのデータ型 – `String`、`Number`、または `Binary`:<br />`S \| N \| B` | はい | 
| `gsiRangeKeyName` | インデックスソートキーの名前。インデックスにシンプルなプライマリキー (パーティションキー) のみがある場合は、このパラメータを指定しないでください。 | いいえ | 
| `gsiRangeKeyType` | インデックスソートキーのデータ型 – `String`、`Number`、または `Binary`:<br />`S \| N \| B`<br /> インデックスにシンプルなプライマリキー (パーティションキー) のみがある場合は、このパラメータを指定しないでください。 | いいえ | 
| `recordDetails` | インデックスキー違反の詳細を出力ファイルに書き込むかどうか。設定が `true` の場合 (デフォルト)、違反項目に関する完全な情報が報告されます。設定が `false` の場合、違反の数のみが報告されます。 | いいえ | 
| `recordGsiValueInViolationRecord` | 違反しているインデックスキーの値を出力ファイルに書き込むかどうか。設定が `true` の場合 (デフォルト)、キー値が報告されます。設定が `false` の場合、キー値は報告されません。 | いいえ | 
| `detectionOutputPath` | Violation Detector の出力ファイルのフルパス。このパラメータは、ローカルディレクトリまたはAmazon Simple Storage Service (Amazon S3) への書き込みをサポートしています。次に例を示します。<br />`detectionOutputPath = ``//{{local/path/filename.csv}}`<br />`detectionOutputPath = ``s3://{{bucket}}/{{filename.csv}}`<br />出力ファイル内の情報は、CSV (Comma-Separated Values) 形式で表示されます。`detectionOutputPath` を設定しない場合、出力ファイル名は `violation_detection.csv` になり、現在の作業ディレクトリに書き込まれます。 | いいえ | 
| `numOfSegments` | Violation Detector がテーブルをスキャンするときに使用される並列スキャンセグメントの数。デフォルト値は 1 です。これは、テーブルがシーケンシャルにスキャンされることを意味します。値が 2 以上の場合、Violation Detector はテーブルをその数の論理セグメントに分割し、同じ数のスキャンスレッドにします。<br />`numOfSegments` の設定は最大 4,096 です。<br />大きなテーブルの場合、並列スキャンはシーケンシャルスキャンよりも一般的に高速です。さらに、テーブルが複数のパーティションにまたがるほど大きい場合、並列スキャンによって読み込みアクティビティが複数のパーティションに均等に分散されます。DynamoDB の並列スキャンの詳細については、「[並列スキャン](Scan.md#Scan.ParallelScan)」を参照してください。 | いいえ | 
| `numOfViolations` | 出力ファイルに書き込むインデックスキー違反の上限値。設定が `-1` の場合 (デフォルト)、テーブル全体がスキャンされます。正の整数に設定すると、Violation Detector は設定した数の違反を検出した後に停止します。 | いいえ | 
| `numOfRecords` | スキャンされるテーブル内の項目数。設定が -1 の場合(デフォルト)、テーブル全体がスキャンされます。正の整数に設定すると、Violation Detector はテーブル内の設定した数の項目をスキャンした後に停止します。 | いいえ | 
| `readWriteIOPSPercent` | テーブルスキャン中に使用される、プロビジョニングされた読み込みキャパシティーユニットの割合を調整します。有効な値の範囲は `1`～`100` です。デフォルト値 (`25`) は、Violation Detector がテーブルのプロビジョニングされた読み込みスループットの 25% 以下を使用することを意味します。 | いいえ | 
| `correctionInputPath` | Violation Detector 修正入力ファイルのフルパス。Violation Detector を修正モードで実行すると、このファイルの内容を使用して、グローバルセカンダリインデックスに違反するテーブル内のデータ項目を変更または削除します。<br />`correctionInputPath` ファイルの形式は、`detectionOutputPath` ファイルの形式と同じです。これにより、検出モードからの出力を修正モードの入力として処理できます。 | いいえ | 
| `correctionOutputPath` | Violation Detector 修正出力ファイルのフルパス。このファイルは、更新エラーがある場合にのみ作成されます。<br />このパラメータは、ローカルディレクトリまたは Amazon S3 への書き込みをサポートします。次に例を示します。<br />`correctionOutputPath = ``//{{local/path/filename.csv}}`<br />`correctionOutputPath = ``s3://{{bucket}}/{{filename.csv}}`<br />出力ファイルの情報は CSV 形式で表示されます。`correctionOutputPath` を設定しない場合、出力ファイル名は `violation_update_errors.csv` になり、現在の作業ディレクトリに書き込まれます。 | いいえ | 

## 検出
<a name="GSI.OnlineOps.ViolationDetection.Detection"></a>

インデックスキー違反を検出するには、Violation Detector を `--detect` コマンドラインオプションで使用します。このオプションがどのように機能するかを示すには、`ProductCatalog` テーブルを検討してください。以下は、テーブルの項目の一覧です。プライマリキー (`Id`) と `Price` 属性のみを表示しています。


****  

| ID (プライマリキー) | 価格 | 
| --- | --- | 
| 101 |  5  | 
| 102 |  20  | 
| 103 | 200  | 
| 201 |  100  | 
| 202 |  200  | 
| 203 |  300  | 
| 204 |  400  | 
| 205 |  500  | 

`Price` の値はすべて、`Number` 型です。ただし、DynamoDB はスキーマレスであるため、数値以外の `Price` の項目を追加することができます。たとえば、`ProductCatalog` テーブルに別の項目を追加するとします。


****  

| ID (プライマリキー) | 価格 | 
| --- | --- | 
| 999 | "Hello" | 

これで、テーブルには合計 9 つの項目が追加されました。

ここで、新しいグローバルセカンダリインデックスをテーブルに追加します: `PriceIndex`。このインデックスのプライマリキーは、パーティションキー `Price` で、`Number` 型です。インデックスが構築されると、8 つの項目が含まれますが、`ProductCatalog` テーブルには 9 つの項目があります。この不一致の理由は、値 `"Hello"` は `String` 型ですが、`PriceIndex` は `Number` 型のプライマリキーを持っているからです。`String` 値がグローバルセカンダリインデックスキーに違反するため、インデックスには存在しません。

この状況で Violation Detector を使用するには、まず次のような設定ファイルを作成します。

```
# Properties file for violation detection tool configuration.
# Parameters that are not specified will use default values.

awsCredentialsFile = /home/alice/credentials.txt
dynamoDBRegion = us-west-2
tableName = ProductCatalog
gsiHashKeyName = Price
gsiHashKeyType = N
recordDetails = true
recordGsiValueInViolationRecord = true
detectionOutputPath = ./gsi_violation_check.csv
correctionInputPath = ./gsi_violation_check.csv
numOfSegments = 1
readWriteIOPSPercent = 40
```

続いて、次の例のように、Violation Detector を実行します。

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --detect keep

Violation detection started: sequential scan, Table name: ProductCatalog, GSI name: PriceIndex
Progress: Items scanned in total: 9,    Items scanned by this thread: 9,    Violations found by this thread: 1, Violations deleted by this thread: 0
Violation detection finished: Records scanned: 9, Violations found: 1, Violations deleted: 0, see results at: ./gsi_violation_check.csv
```

`recordDetails` 設定パラメータが `true` に設定されている場合、Violation Detector は、次の例のように、各違反の詳細を出力ファイルに書き込みます。

```
Table Hash Key,GSI Hash Key Value,GSI Hash Key Violation Type,GSI Hash Key Violation Description,GSI Hash Key Update Value(FOR USER),Delete Blank Attributes When Updating?(Y/N) 

999,"{""S"":""Hello""}",Type Violation,Expected: N Found: S,,
```

出力ファイルは CSV 形式です。ファイルの最初の行はヘッダーで、2 行目以降はインデックスキーに違反する項目ごとに 1 つのレコードが続きます。これらの違反レコードのフィールドは次のとおりです。
+ **テーブルハッシュキー** – テーブル内の項目のパーティションキー値です。
+ **テーブル範囲キー** – テーブル内の項目のソートキー値です。
+ **GSI ハッシュキー値** – グローバルセカンダリインデックスのパーティションキー値です。
+ **GSI ハッシュキー違反タイプ** – `Type Violation` または `Size Violation`。
+ **GSI ハッシュキー違反の説明** – 違反の原因です。
+ **GSI ハッシュキー更新値 (ユーザー用)** – 修正モードでは、ユーザーが指定した属性の新しい値。
+ **GSI 範囲キー値** – グローバルセカンダリインデックスのソートキー値です。
+ **GSI 範囲キー違反タイプ** – `Type Violation` または `Size Violation`。
+ **GSI 範囲キー違反の説明** – 違反の原因です。
+ **GSI 範囲キーの更新値 (ユーザー用)** – 修正モードでは、ユーザーが指定した属性の新しい値。
+ **更新時に空白の属性を削除 (Y/N)** – 修正モードでは、テーブル内の違反項目を削除する (Y) か、保持する (N) かを決定します。ただし、次のフィールドのいずれかが空白の場合のみ実行されます。
  + `GSI Hash Key Update Value(FOR USER)`
  + `GSI Range Key Update Value(FOR USER)`

  これらのフィールドのいずれかが空白でない場合、`Delete Blank Attribute When Updating(Y/N)` は何も実行しません。

**注記**  
出力形式は、設定ファイルとコマンドラインオプションによって異なることがあります。例えば、テーブルに単純なプライマリキー (ソートキーなし) がある場合、出力にはソートキーフィールドは表示されません。  
ファイル内の違反レコードがソート順でない可能性があります。

## 修正
<a name="GSI.OnlineOps.ViolationDetection.Correction"></a>

インデックスキーの違反を修正するには、Violation Detector を `--correct` コマンドラインオプションで使用します。修正モードでは、Violation Detector は `correctionInputPath` パラメータで指定された入力ファイルを読み込みます。このファイルの形式は `detectionOutputPath` ファイルの形式と同じなので、検出からの出力を修正のための入力として使用できます。

Violation Detector には、インデックスキー違反を修正する 2 種類の方法があります。
+ **違反の削除** – 属性値に違反しているテーブル項目を削除します。
+ **違反の更新** – テーブル項目を更新し、違反している属性を違反しない値に置き換えます。

いずれの場合も、検出モードからの出力ファイルを修正モードの入力として使用できます。

`ProductCatalog` の例に進みます。違反している項目をテーブルから削除するとします。これを行うには、次のコマンドラインを使用します。

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --correct delete
```

この時点で、違反している項目を削除するかどうかを確認するメッセージが表示されます。

```
Are you sure to delete all violations on the table?y/n
y
Confirmed, will delete violations on the table...
Violation correction from file started: Reading records from file: ./gsi_violation_check.csv, will delete these records from table.
Violation correction from file finished: Violations delete: 1, Violations Update: 0
```

`ProductCatalog` と `PriceIndex` の項目の数が同じになりました。