LoginSignup
1
0

More than 5 years have passed since last update.

CloudFront のキャッシュを削除して再生成する ( JavaScript SDK )

Last updated at Posted at 2018-11-20

AWS CloudFront のキャッシュを JavaScript SDK を使って削除した後に再生成するスクリプトのメモです。

背景

CloudFront でキャッシュはしたい。キャッシュはしたいけど、データは更新される。更新されたらキャッシュも更新したい。キャッシュを削除すれば良いけど、そうすると最初のアクセス時にキャッシュ生成コストを負担することになる。その負担をユーザに課したくない。つまり、削除後に再生成するようにしたい。

やり方

やり方は本当に単純で CloudFront の SDK を使って、指定 URL のキャッシュを削除して、HTTP リクエストでアクセスしてキャッシュを生成するっていうだけです。気をつけなきゃいけないのは CloudFront のキャッシュ条件程度と、HTTP リクエストのタイミングくらいです。

CloudFront キャッシュ削除の反映

CloudFront は現時点 ( 2018/11/20 ) でキャッシュを削除してから約5秒程度でキャッシュが削除されます。

(*) 90%程度のエッジサーバは5秒、全部に完全に反映するには1〜2分程度というアナウンスが AWS からあったようですがオリジナルソースの URL が今は NotFound 状態で公式アナウンスはありません…

というわけで、5秒後くらいに HTTP リクエストしてあげればキャッシュを再生成できるということになります。

コード

const got = require('got')
const AWS = require('aws-sdk')
const cloudfront = new AWS.CloudFront({
  apiVersion: '2018-06-18',
  accessKeyId: 'ACCESS_KEY',
  secretAccessKey: 'SECRET_KEY',
})

(() => {
  const invalidCache = async () => {
    const timestamp = new Date()
    const string_timestamp = String(timestamp.getTime())
    const invalidate_items = ['/example.json']
    const item_count = invalidate_items.length
    const params = {
      DistributionId: 'DistributionId',
      InvalidationBatch: {
        CallerReference: string_timestamp,
        Paths: {
          Quantity: item_count,
          Items: invalidate_items
        }
      }
    }
    await cloudfront.createInvalidation(params).promise()
    setTimeout(() => {
      got('https://example.com/example.json', {
        headers: {
          'Origin': 'https://example.com'
        }
      })
    }, 5000)
  }
  invalidCache()
})()

createInvalidation でキャッシュ削除を開始します。Promise の完了時に削除自体が完了しているわけではなく、あくまでスタックに積んだ状態です。なので、そこからおおよそ5秒後には消えているだろうという見積もりで got ライブラリを使って HTTP リクエストをしています。

その際に、CloudFront で設定した Headers の値と一致するようなリクエストパラメータにする必要があります。今回のコードだと CORS のために Origin ヘッダーをホワイトリストに追加しているので Origin ヘッダーを追加しています。

Invalidation のステータスをポーリング

キャッシュ削除が完全に完了したかどうかを知るには数秒おきにステータス確認するしかないです。2秒おきに getInvalidation を叩いて該当 ID のステータスを取得しています。ちなみに実際に試してみると InProgress から Completed になるまで約2分弱かかりました。

const checkStatus = (id) => {
  const params = {
    DistributionId: 'DistributionId',
    Id: id
  }
  cloudfront.getInvalidation(params, (err, data) => {
    console.log(err, data)
  })
}

const invalidCache = () => {
  const timestamp = new Date()
  const string_timestamp = String(timestamp.getTime())
  const invalidate_items = ['/example.json']
  const item_count = invalidate_items.length
  const params = {
    DistributionId: 'DistributionId',
    InvalidationBatch: {
      CallerReference: string_timestamp,
      Paths: {
        Quantity: item_count,
        Items: invalidate_items
      }
    }
  }
  cloudfront.createInvalidation(params, (err, data) => {
    const id = data.Invalidation.Id
    setInterval(checkStatus, 2000, id)
  })
}

以上です。完全ではないですが実用性はそれなりにあると思います。実際には AWS Lambda などを使って、データベース更新コールバックなどで実行するような感じになると思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0