【Laravel】キャッシュドライバをDynamoDBに変更した際にある問題に遭遇した話
こんにちは、モリです。 今回は、Laravelで利用していたキャッシュドライバ(キャッシュのストア)をRedisからDynamoDBに変更した際に遭遇した問題について取り上げたいと思います。
環境
- PHP: 8.1
- Laravel Framework: 9.44.0
問題の背景と原因
まずキャッシュドライバの切り替えから問題が起こるまでの状況を説明します。 RedisからDynamoDBへの変更はいたって簡単です。
use Illuminate\Cache\Repository;
/** @var Repository $repository */
$repository = Cache::store('dynamodb');
store()によってキャッシュドライバを切り替えることができます。
DynamoDBに切り替えた中に、0始まりを含むランダムな数字の並びを保存していた箇所も含まれていました。 例えば下記のようなコードによって生成される値を保存していました。
$number = random_int(0, 9999999);
$sevenDigitsCode = sprintf('%07d', $number); // ex.0358432
しかしRedisでは問題なく保存されていた値が、DynamoDBに保存されると問題が発生することに気づきました。
// Redis
$repository = Cache::store('redis');
$repository->put('key', '0000123', 60);
$repository->get('key'); // 0000123
// DynamoDB
$repository = Cache::store('dynamodb');
$repository->put('key', '0000123', 60);
$repository->get('key'); // 123
0始まりの値の場合、0が除かれて保存されてしまっています。 この問題の原因を突き止めるために、保存処理をみてみたいと思います。
Cache::store('dynamodb')
で生成されたインスタンスは、 Illuminate\Cache\DynamoDbStoreクラスのインスタンスがコンストラクタインジェクションによって注入されており、保存や取得などの実際の処理はこのDynamoDbStoreの中身になります。
public function put($key, $value, $seconds)
{
$this->dynamo->putItem([
'TableName' => $this->table,
'Item' => [
$this->keyAttribute => [
'S' => $this->prefix.$key,
],
$this->valueAttribute => [
$this->type($value) => $this->serialize($value),
],
$this->expirationAttribute => [
'N' => (string) $this->toTimestamp($seconds),
],
],
]);
return true;
}
protected function type($value)
{
return is_numeric($value) ? 'N' : 'S';
}
保存処理は上記のようになっています。
DynamoDBの仕様によって保存する値の型を指定する必要があり、type
メソッドによって保存する値の型を判定しています。 こちらとしては値を0始まりの文字列として扱ってほしいのですが、is_numeric
によって保存する値が文字列(S)ではなく数値(N)と勝手に判定され、0が除外されて保存されます。
以上が問題の原因でした。これに抗う術はなく、型指定する方法も用意されていないため、値に対して再度0埋めすることがとりあえずの解決策となります。
また面倒ですが、こちらの方法で独自のキャッシュドライバを自分で定義することもできるので、その場合は型を指定できるように処理を書いてあげれば解決できるでしょう。
最後に
以上が、キャッシュドライバをDynamoDBに変更した際に遭遇した問題についての紹介でした。 各キャッシュのストアがIlluminate\Contracts\Cache\Store
インターフェースを実装しているため、キャッシュドライバを切り替えることが容易にできるのは便利ですが、その分実装の違いによって問題が発生することもあるので注意が必要です。
ありがとうございました。