DynamoDBのパーティションキーを変更した
背景
僕が個人開発したアプリで不具合が出るというので、ログを見ていたところ以下のエラーを発見しました。
pynamodb.exceptions.ScanError: Failed to scan table: An error occurred
The level of configured provisioned throughput for the table was exceeded.
Consider increasing your provisioning level with the UpdateTable API.
DynamoDBを使用しているのですが、画像URLを保存するテーブルで、読み取り時のキャパシティが設定値を超えてるため、発生しておりました。一時的にキャパシティを増やしたのですが、他のテーブルより明らかに消費量が多かったので、コードを見直してみました。
分かる人から見ると、ツッコミどころしかない、フルスキャンしまくり最悪の設計をしていました。
def find_by_user(user_id: str):
sakes = list(Sake.scan(Sake.user_id == user_id))
for sake in sakes:
sake.images = list(SakeImage.scan(SakeImage.sake_id == sake.id))
return sakes
しかも、sake_imageテーブルはパーティションキーがidでした。
この問題を解決するには、sake_imageのデータモデルを変更する必要があることが分かりました。
ただし、Dynamoで一度作ったテーブルのパーティションキーは後から変更できないようになっていました。(この時点で絶望してました。)
対応策を練る
後から変更できないとなると、テーブルの作り直ししか手はないです。
コンソールを眺めていると、S3へのエクスポート、S3へのインポートがありました。
試しにテストしてみました。
sake_image_bkという新テーブルを作成して、sake_imageからインポート。
新テーブルはhash_key: sake_id, range_key: id
です。
上手くインポートできました!
キーの変更のみであれば、データの加工は必要なさそうです。
エクスポートされたデータを見ると分かるのですが、json形式で全データが吐き出されています。
これでどうにかなりそう。
もう一つ課題が生まれました。
どうやってユーザの操作を停止させよう?
慌ててメンテナンスページを作成いたしました。
全くドキュメントや作成例がなかったのですが、我ながらいい設計で出来ました。
ここは別途記事にしたいと思います。
テーブルの作り直し
これで、必要な準備が出揃いました。
まずは、ローカルでデータモデルを変更してみました。
Before
sake: hash_key: id
sake_image: hash_key: id
After
sake: hash_key: user_id, range_key: id
sake_image: hash_key: sake_id, range_key: id
これでフルスキャンせずに、パーティションキーを使って取得することができるようになりました。
def find_by_user(user_id: str):
sakes = list(Sake.query(user_id))
for sake in sakes:
sake.images = SakeImage.query(sake.id)
return list(sakes)
あとは本番環境に適用して終わりです。
ガチガチに手順を作成しました。笑
完全に仕事モードです。テーブルの移行は毎回緊張します。
何とか無事に移行できました。
本題に戻り、sake_imageのスループットですが、以前はMaxで6.7まで行ってました。
なんと、0.38まで減った!!
まとめ
いくら個人開発とはいえ、事前の調査不足でした。。。
これデータ数が多いと、取り返しのつかないことになってました。
何とか出来たのは本当に良かったです。
これも一つの経験と思うことにします。
Discussion