🤖

DynamoDBのパーティションキーを変更した

2024/11/21に公開

背景

僕が個人開発したアプリで不具合が出るというので、ログを見ていたところ以下のエラーを発見しました。

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