前提
CloudFront のキャッシュを利用するシステムで、ユーザーの操作をトリガーに、条件に合致するオブジェクトパスに invalidation リクエストする仕様。
困ったこと
com.amazonaws.services.cloudfront.model.TooManyInvalidationsInProgressException: Processing your request will cause you to exceed the maximum number of in-progress wildcard invalidations.
進行中の invalidation プロセスにおいて、ワイルドカード (*
) を含むオブジェクトパスの指定は最大15個までで、その上限を超えるとエラーが返る模様。
同時無効化リクエストの最大制限 | ファイルの無効化 - Amazon CloudFront
たまに CloudFront 側で進行中の invalidation が滞留することがある。invalidation のオブジェクトパスにワイルドカード (*
) を含む場合に、表題のエラーが発生することがある。特に、同一ユーザーが連続して操作した際に、同様の invalidation プロセスが複数生成されてしまうのが痛い...。
対策
今回は、invalidation の CallerReference を利用してエラーの発生を抑えてみる。
単位時間あたりの同じオブジェクトパスに対する invalidation において、CallerReference に同じ値を指定することによって短時間で同じオブジェクトパスに対する invalidation が実行できなくなる。正確には、invalidation リクエストは投げるが、CloudFront 側で invalidation プロセスが生成されない。
CallerReference にどういう値を指定するか
このドキュメントの CallerReference に書かれているように、単純にタイムスタンプを指定するだけだと別々のユーザーが同時に操作した場合に invalidation がコンフリクトする可能性がある。というわけで、ユーザーのリクエスト情報を CallerReference に含める。
今回は、単位時間を1分間として、リクエストパラメータのオブジェクトパスを並び替えて結合したものを利用する。最終的には yyyyMMddHHmm_{param1}-{param2}-...
のような文字列を CallerReference に指定する。
サンプルコードはこちら。
private String callerReference(List<String> params) { var str = String.format("%s_%s", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")), String.join("-", params.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList())) ); return str.substring(0, Math.min(str.length(), 128)); // max 128 characters }
単位時間を3分間にするならこんな感じ。
var currentDateTime = LocalDateTime.now(); var rounded = currentDateTime.withMinute(currentDateTime.getMinute() - (currentDateTime.getMinute() % 3)).format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
これで、短時間で同様の invalidation は実行されなくなるので、表題のエラー抑止にも効果がありそう。
補足)
com.amazonaws.services.cloudfront.model.InvalidArgumentException: The parameter CallerReference is too big.
CallerReference に指定できるのは最大128文字までの模様...。仕方ないので128文字で切り出している。