AWS Kinesisのシャード割り当てのルール

Kinesisにデータを送る際にシャードを固定したくなりまして。

どういう仕組みで割り当てられているのか分かりづらく、誰か図示でもしてくれてれば理解早かったのになーと思ったので書きます。

# AWS Kinesis
とりあえずデータを送りつけるところ。詳しいことは別の何かを見てください！

LambdaとかS3とかDynamoDBとか、その後のデータの処理はお好きにどうぞ。

# シャード
>シャードは、ストリーム内のデータレコードの一意に識別されたグループです。ストリームは複数のシャードで構成され、各シャードが容量の 1 単位になります。各シャードは 読み取りは最大 5 transactions per second、データ読み取りの最大合計レートは 2 MB per second と 書き込みについては最大 1,000 records per second、データの最大書き込み合計レートは 1 MB per second (including partition keys) をサポートできます。ストリームのデータ容量は、ストリームに指定したシャードの数によって決まります。ストリームの総容量はシャードの容量の合計です。

参考: https://docs.aws.amazon.com/ja_jp/streams/latest/dev/key-concepts.html

とのことです。

まあ、いっぱいデータ送られてくるのを並列で処理したいときはシャード増やしましょう、っていう話です。

シャードが多いほど、お金はかかるのでその点は要注意です。

# 分割のルール
本題。

Kinesisでは、Kinesisに対してデータを送信する元が「パーティションキー」という値を使ってデータを投げつけています。

>パーティションキーは、ストリーム内のデータをシャード単位でグループ化するために使用されます。Streams サービスでは、ストリームに属するデータレコードを複数のシャードに配分するとき、各データレコードに関連付けられたパーティションキーを使用して、配分先のシャードを決定します。パーティションキーは最大 256 バイト長の Unicode 文字列です。MD5 ハッシュ関数を使用してパーティションキーを 128 ビットの整数値にマッピングし、関連付けられたデータレコードをシャードにマッピングします。パーティションキーは、ストリームにデータを入力するアプリケーションによって指定されます。

参考: https://docs.aws.amazon.com/ja_jp/streams/latest/dev/key-concepts.html

Kinesis側は、このパーティションキーに応じて、どのシャードに割り当てるかを決めてるというわけです。

>128ビットの整数値

こいつ10進数のことみたいです。
整数ってだけだと何進数か判断できなくてちょっと悩みました。普通MD5って16進数だしね。

で、その割り当てのルールはというと。

パーティションキー: 最大256バイトのUnicode文字列
↓
Hash化して128bitの整数値にマッピング(ハッシュキーと呼ばれてます)
↓
Hash化された整数値(ハッシュキー)が割り当てられたシャードにデータが送られる

的な感じです。

MD5の最大値は16進数で
`FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF`
なので、これを10進数にすると
`340282366920938463463374607431768211455`
なわけです。

数えられんわ。

とにかく、こうして128bitの10進数が得られました。

Kinesisのシャードは、1シャードの場合 `0~340282366920938463463374607431768211455` のハッシュキーが割り当てられています。

0から最大値ということですね。どんなパーティションキーがきても、hash化された値は最大で `340282366920938463463374607431768211455` なので、必ず1つのシャードに送られます。

最大値をmとし、均等に分割すると、シャードを2分割する場合は0~m/2, m/2+1~mの2つに、3分割する場合は0~m/3, m/3+1~2*m/3, 2*m/3+1~mの3つに分けることになります。

均等である必要はないと思いますが、特別な理由が無い限りは均等に配分して良いのではないでしょうか。

もうちょっと具体的に言うと、2つに分割したい場合は、340282366920938463463374607431768211455を半分にしてあげれば良いので、

shard1: `0 ~ 170141183460469231731687303715884105727`
shard2: `170141183460469231731687303715884105728 ~ 340282366920938463463374607431768211455`

に割り当てます。
4つに均等に分割したい場合は、

shard1: `0 ~ 85070591730234615865843651857942052863`
shard2: `85070591730234615865843651857942052864 ~ 170141183460469231731687303715884105727`
shard3: `170141183460469231731687303715884105728 ~ 255211775190703847597530955573826158591`
shard4: `255211775190703847597530955573826158592 ~ 340282366920938463463374607431768211455`

となります。

(桁数の多い計算には http://keisan.casio.jp/calculator こちらを利用させていただきました。)

## 具体例
例として、 `partition-key-0001` という文字列をパーティションキーとした場合に、均等に4分割されたシャードのどこに割り当てられるかを図示したものを貼ります。

![cloudcraft - kinesis_shard.png](https://qiita-image-store.s3.amazonaws.com/0/67253/2f5445b0-0726-4c7f-5d7b-81b1f21681ee.png)

1. `partition-key-0001` という文字列をパーティションキーとする
2. MD5でハッシュ化すると、 `b7681e2243f62f440887b6d38c002537` という16進数の値が得られる
3. 2の16進数を10進数にすると `243789333289005976465737331408549979447` となる
4. ３の結果は上記shard1~shard4の範囲のうちshard3に該当しているので、パーティションキーに `partition-key-0001` を指定したデータはshard3に投げられる

となります。

ちなみに私が現在所属しているプロジェクトでは、userごとにshardを固定したかったので、userIdをそのままパーティションキーとしています。

誰かの参考になれば幸いです。

おしまい。