Adobe Commerce / Magento Open SourceとESIを理解する

この記事は「Adobe Commerce / Magento Open Source 2.4.6-p3 / 2.4.5-p5 / 2.4.4-p6がリリースされました」の補足記事です。
Adobe Commerce / Magento Open Sourceには元々の機能としてESI(Edge Side Includes)に対応する機能が用意されています。
この記事ではESIを利用するための条件や使い所について解説していきたいと思います。

ESIとは

ESI(Edge Side Includes)は、2001年頃にAkamaiやOracleなどによって策定された規格で、CDNなどのキャッシュサーバー側での動的なコンテンツ組み立てを実現するための仕組みです。
規格策定から20年以上が経過しており、相応に枯れた技術仕様となっています。利用する側としては比較的シンプルな構成での利用が可能ですが、キャッシュの有効期限や個人情報の取り扱いなどについては十分配慮が必要です。最近のモダンなWebアプリケーション構成との併用も可能ですが、その場合はESIに配慮した調整を要することもあるようです。
またESIは対応しているCDNサービスが限られるため、導入の前にCDNサービス側の対応を確認することが不可欠となっています。

MagentoフレームワークとESI

2015年にMagento2系がリリースされた際、キャッシュサーバーとしてVarnishが指定されるようになりました。
このときにESI対応が含まれるようになり、Adobe Commerce / Magento Open SourceでもESIが利用できるようになりました。

ただし、標準ではESIを使用している箇所は以下の3箇所に限定されています。

  • カテゴリナビ
  • 新着商品ウィジェット
  • 商品ウィジェット

ESIに対応しているからといって、広範囲に活用しているわけではないのが現状です。

ESIを利用するには

Adobe Commerce / Magento Open SourceでESIを利用するためには、以下の条件を揃える必要があります。

  • フルページキャッシュまたはCDNにESI対応の製品・サービスを使用すること
  • 利用したいCDN側(標準ではVarnish または Fastly)でESIが有効であること
  • レイアウトXML上で、ESIに使用したい要素に「ttl="キャッシュ保持期間"」をつけて定義すること
  • ESIで使用したいブロックに Magento\Framework\DataObject\IdentityInterface を実装すること

これらが全て揃っている状況で、MagentoフレームワークではESIを利用することができます。

ESI利用時の挙動

ESIが利用できる状況下では、Magentoが生成するHTML上でESI対象のブロックは下記のようなESIタグとして出力されます。

<esi:include src="http://domain.com/page_cache/block/esi/blocks/%5B%22catalog.topnav%22%5D/handles/エンコードされたハンドル情報/"/>

このようなESIタグが含まれるHTMLは、ESIに対応しているCDNによって次のような処理を経てクライアントに送信されます。

  1. CDN側からESIタグで指定されているURLにリクエストを送信する
  2. アプリケーション(本記事ではAdobe Commerce / Magento Open Source)側が処理を行い、CDN側へレスポンスを送信する。
  3. CDNがESIタグをレスポンス内容で置換し、クライアントに送信する

ESIが有効な場合はページ全体の処理を行うのではなく、ESI対象の一部分だけをアプリケーション側が処理すれば良い状態になるため、構成によってはアプリケーション側の負荷を大きく低減できる可能性があります。
では、もう少し詳しくMagentoフレームワーク側の挙動を見ていきましょう。

リクエストの受付

ESI用のリクエストは、Magento\PageCache\Controller\Block\Esi.php が受け取ります。
このコントローラクラスは、

  • blocks
  • handles

の2つのパラメータを受け取り、どのようなコンテンツを描画すればよいかを判断し、処理します。

描画対象コンテンツの判定

描画対象コンテンツは、blockパラメータとhandleパラメータから判断されます。

blockパラメータの解釈

blockパラメータは先の例の場合、

%5B%22catalog.topnav%22%5D

となっています。この値を変換すると、

["catalog.topnav"]

という配列データになります。
この例では1つのブロックしか含まれていませんが、実装上は複数のブロックを含むことが可能です。

handleパラメータの解釈とHTMLの生成

次にhandleパラメータを解釈します。
handleパラメータはBASE64エンコードされた文字列となっていて、変換後には

["default","cms_index_index","cms_page_view"]

といったレイアウトハンドルが配列の形となって得られます。
その後、これらのレイアウトハンドルに対応するXML定義を読み込み、blockパラメータで指定したブロックのHTMLを得ます。

得られたHTMLはレスポンスとしてCDNへ送られ、元々のHTMLとマージされてクライアントへ送られます。
このとき、レイアウトXMLで指定したttl属性の値がHTTPのExpiresヘッダの値としてセットされ、CDN側に渡されます。

ESIを使用する際の注意点

Adobe Commerce / Magento Open SourceのESIに対する実装では、

  • 描画対象のブロックの指定
  • 描画対象のレイアウトハンドルの指定

はできます。
ですが、

  • 描画対象ブロックに与えるパラメータ

については通常のリクエスト変数(Cookieなども含む)と同じものを使用します。

標準実装でESIを利用しているカテゴリナビでは、 アクセスされたカテゴリなどに基づいてメニュー項目のハイライトを行います。
ところが、ESIを使うと現在アクセスしているカテゴリの判断が適切に行われないことが発生する場合があります。

また、Adobe Commerce / Magento Open Sourceでよく利用するVarnish/Fastlyでは、ESIタグのTTL(キャッシュ有効期限)定義がありません。
ESI部分に対するキャッシュ有効期限を定義する場合は、VCL側で調整することになります。この点を忘れていると、思わぬ落とし穴にハマる可能性があります。

ESIを利用できそうな箇所

では、ESIを利用できそうな箇所はどこなのか?というと、これはなかなかAdobe Commerce / Magento Open Source上では断言しにくいのが実情です。
というのも、

  1. Varnish/FastlyのESI実装が荒っぽい(ESIの技術仕様を完全に網羅していない)
  2. 無料版のVarnishはESIリクエストを並列処理出来ない(遅いESIリクエストがあるとそこで詰まる。別途vmodが必要。)
  3. 無料版のVarnishはESIタグにhttps始まりのURLを指定できない
  4. ESI部分のコンテンツが、想定した通りの生成でないことがある(例:カテゴリナビ)

という理由があるからです。

特に4の事象が

  • ログイン状態の有無による想定外のコンテンツ描画の問題
  • エラーによるコンテンツ欠落がサービス提供に影響する

を引き起こすことがあるため、

  • 描画に要する前提条件がすくない
  • そのコンテンツが万一欠落しても悪影響がない

ものに限定するのが良いのではないかと思います。

そういう意味では、標準で用意されている

  • 新着商品ウィジェット
  • 商品ウィジェット

のようなものはESI対象に向いていると言えるでしょう。

 

2.4.4-p6/2.4.5-p5/2.4.6-p3で追加された設定値

最後に、2.4.4-p6/2.4.5-p5/2.4.6-p3で追加された設定値について紹介しておきましょう。
この設定値は、ESI用のリクエストで利用されている、

handles

パラメータの上限を定義するものです。
デフォルト値では100となっていて、100件のレイアウトハンドルを指定できます(もちろんもっと少なくしても構いません)。
設定値の上限を超える数のレイアウトハンドルについては読み込みが行われず、無視される作りになっています。

過去のセキュリティアップデートでは、レイアウトハンドル名のパターンをチェックする処理が追加されており、不正な名称のレイアウトハンドルは指定できなくなっています。
とはいえ、異常な数のファイル読み込みを阻止するという意味では、この設定値は一定の効果があると言えます。