タグアーカイブ パフォーマンス

Shield SecurityのcookieがNginxキャッシュを止める時の解決策

Shield SecurityのcookieがNginxキャッシュを止める時の解決策

Shield Security の icwp-wpsf-notbot cookie がサーバーのページキャッシュを妨害する問題は、Shield の設定で「silentCAPTCHA」の複雑度を「なし」にし、かつカスタムフィルターで匿名ユーザーへの cookie 送信を停止することで解決できる。

なぜ Shield Security が全ページでキャッシュを止めてしまうのか

なぜ Shield Security が全ページでキャッシュを止めてしまうのか

Shield Security はボット判定や silentCAPTCHA の動作のために icwp-wpsf-notbot という cookie をフロントエンドの全ページで発行する仕様になっている。Nginx のキャッシュ機構は原則として Set-Cookie ヘッダを含むレスポンスをキャッシュしないため、この cookie がすべてのページキャッシュを無効化してしまう。結果として x-proxy-cache: MISS が返り続け、サーバーへのリクエストが毎回発生し、レスポンスタイムが 1000ms を超える状況に陥る。

Before(Shield 有効時)
リクエストのたびに Set-Cookie が発生
Nginx はレスポンスをキャッシュせず破棄
レスポンスタイム 1000ms〜1600ms
After(cookie 停止後)
キャッシュ可能なレスポンスが返る
初回 MISS → 2回目以降 HIT で高速表示
レスポンスタイム 約100ms
cookie がキャッシュを妨害している状態  cookie 停止後

サーバー側で特定の cookie だけをキャッシュ対象から除外する設定ができない場合、プラグイン側でこの cookie を止めるのが唯一の現実的な解決策になる。

管理画面で silentCAPTCHA の複雑度を「なし」にする

管理画面で silentCAPTCHA の複雑度を「なし」にする

Shield Security の silentCAPTCHA は、ボット防御のためにフロントエンドのページにも cookie をセットする。設定を最小限に絞り込むには、まず管理画面から操作する。

STEP 1 WordPress 管理画面 → 左メニュー「Shield」→「設定」
STEP 2 「CAPTCHA」タブを開き「silentCAPTCHA」セクションを探す
STEP 3 「複雑度」を なし に変更して保存
STEP 4 「ログイン保護」「スパムボットブロック」などで不要な silentCAPTCHA の利用をすべてオフにする

これだけでは cookie の出力が止まらないケースが多い。Shield は silentCAPTCHA の設定に関わらず、フロントエンドの訪問者に対して一律に icwp-wpsf-notbot をセットする内部ロジックを持っているためだ。ここから先はコードレベルの対応が必要になる。

フィルターフックで匿名ユーザーへの cookie 送信を無効化する

フィルターフックで匿名ユーザーへの cookie 送信を無効化する

Shield Security は icwp-wpsf-notbot cookie を制御するための専用フィルターを提供している。テーマの functions.php に数行のコードを追加すれば、ログインしていない一般訪問者に対する cookie の送信だけを停止できる。

functions.php に追加するコード

以下のコードを子テーマの functions.php に追加する。子テーマを使用していない場合は、Code Snippets プラグインなどで追加してもよい。テーマの直接編集はアップデートで消えるため避ける。

/**
 * Shield Security の icwp-wpsf-notbot cookie をログインしていないユーザーには送信しない
 */
add_filter( 'icwp_shield_set_notbot_cookie', function( $set_cookie ) {
    if ( ! is_user_logged_in() ) {
        return false;
    }
    return $set_cookie;
} );

このフィルターは icwp-wpsf-notbot cookie をセットする直前に呼び出される。ログインしていないユーザーの場合は false を返して cookie の発行をブロックし、ログイン済みユーザーには通常通り cookie を許可する。管理画面のログイン保護や IP ブロック、ファイアウォールなどのコア機能には影響しない。

動作確認の手順

  • シークレットウィンドウでサイトにアクセスする
  • ブラウザの開発者ツール(F12)→「アプリケーション」タブ→「Cookie」で icwp-wpsf-notbot が存在しないことを確認する
  • レスポンスヘッダーから Set-Cookie が消えていることを確認する
  • 2回目以降のアクセスで x-proxy-cache: HIT が返ることを確認する

全プラグインを最新に保って不要な干渉を防ぐ

全プラグインを最新に保って不要な干渉を防ぐ

Shield Security はアップデートの頻度が高く、バージョンによって内部のフィルター名が変更されることがある。Shield 22.1.3 および WordPress 7.0.1、PHP 8.2 環境では上記のフィルターが有効だが、プラグインが更新された際にはフィルター名が維持されているかを確認する必要がある。

また、キャッシュ系プラグインや CDN を併用している場合は、cookie 停止後にキャッシュを完全にクリアしてからテストすること。古いキャッシュが残っていると HIT になっていてもレスポンスが遅いままに見える場合がある。

よくある質問

SilentCAPTCHA を無効にしただけでは cookie は消えないのか

多くの場合、管理画面の設定だけでは cookie の出力は止まらない。Shield は silentCAPTCHA が無効でもフロントエンドの全リクエストに cookie をセットする内部挙動を持っている。確実に止めるにはフィルターフックの追加が必要だ。

このフィルターでログイン保護やファイアウォールは機能しなくなるか

今回のコードは匿名ユーザーへの icwp-wpsf-notbot cookie 送信だけを止めるもので、IP ブロックやブルートフォース保護、ファイアウォールといった Shield の主要防御機能はすべて通常通り動作する。ログインしたユーザーには引き続き cookie がセットされる。

functions.php を直接編集するのはリスクがないか

テーマの functions.php を直接編集すると、テーマのアップデートで変更が失われる。必ず子テーマを作成するか、Code Snippets のようなコード管理プラグインを使用する。また、コード追加前にサイトのバックアップを取得しておくと安全だ。

フィルターが効かない場合の確認ポイントは

Shield のバージョンが古い、あるいは逆に新しすぎてフィルター名が変更されている可能性がある。Shield の公式ドキュメントや変更履歴を確認する。また、キャッシュ系プラグインでサーバー側のキャッシュとは別にページキャッシュが残っていると、cookie 停止後も古いレスポンスが返り続けるため、すべてのキャッシュをクリアしてからテストする。

レンタルサーバーのキャッシュ設定で cookie ごとの除外はできないのか

共用サーバーの Nginx キャッシュ設定はサーバー全体で一律に適用されることが多く、特定の cookie だけを除外する柔軟な設定は提供されないケースがほとんどだ。サーバー側で対応できない以上、プラグイン側で cookie を止めるのが最も確実な方法になる。

この記事のポイント

  • Shield Security の icwp-wpsf-notbot cookie が Nginx のページキャッシュを全面的に阻害する
  • 管理画面で silentCAPTCHA の複雑度を「なし」にしても cookie は止まらない
  • functions.php にカスタムフィルターを追加しログインしていないユーザーへの cookie 送信を停止する
  • フィルター追加後はシークレットウィンドウで Set-Cookie ヘッダーと x-proxy-cache の値を確認する
  • プラグインのアップデート後はフィルター名の変更に注意しキャッシュを完全クリアして再テストする
Cloudflare Workers Cache登場、Worker専用キャッシュでコスト削減

Cloudflare Workers Cache登場、Worker専用キャッシュでコスト削減

Workers Cache の登場でCloudflare Workersが「オリジン」から「静的配信」へ進化

Workers Cache の登場でCloudflare Workersが「オリジン」から「静的配信」へ進化

CloudflareがWorkers Cacheを正式にリリースした。これは単なるキャッシュ機能の追加ではない。Workersのアーキテクチャを根本から覆し、コストとパフォーマンスのトレードオフを解消する大きな転換点だ。一言で表せば「あなたのWorkerの前に、そのWorker専用のキャッシュを置ける機能」である。

1行の設定を追加するだけで、Workerが生成したレスポンスはCloudflareのエッジネットワークにキャッシュされる。キャッシュが有効な間はWorkerそのものが実行されず、CPU時間の課金もゼロになる。これは特に、サーバーサイドレンダリング(SSR)を行うアプリケーションにとって、待望のソリューションだ。

本記事では、なぜこの機能が必要とされていたのか、具体的に何が変わるのか、そして開発者がどのように活用できるのかを詳しく解説する。

従来のモデル(Before)
ユーザー Worker キャッシュ オリジン
Workerがリクエストの最前線に立つモデル。Workerはリクエストを処理した後、オリジンサーバーとキャッシュレイヤーに問い合わせる。
問題: Workerがオリジンの場合、処理のたびにコードが実行され、キャッシュの恩恵を受けにくい。
Workers Cache モデル(After)
ユーザー Workers Cache Worker
Workerの前に、そのWorker専用のキャッシュが配置される。キャッシュヒット時はWorkerが実行されず、Cloudflareのエッジから直接レスポンスが返る。
効果: レスポンスが高速化し、WorkerのCPU実行コストが削減される。
従来の処理フロー(Workerが起点)  Workers Cache導入後(キャッシュが起点)

この図が示すように、Workers Cacheはリクエストの最前線に立つ。これにより、Workerが事実上のオリジンサーバーとして振る舞う現代的なアプリケーションのパフォーマンスとコスト構造が劇的に改善される。

なぜサーバーサイドアプリに「Workerの前のキャッシュ」が必要だったのか

なぜサーバーサイドアプリに「Workerの前のキャッシュ」が必要だったのか

Workerが「経由点」から「オリジン」へ変わった世界

2017年のリリース当初、Cloudflare Workersはオリジンサーバーの手前でリクエストを書き換える「中間処理層」として設計された。A/Bテストの振り分けやヘッダーの追加といった、軽量な処理をエッジで実行するユースケースが中心だったのだ。当時、Workerはキャッシュよりもさらにオリジンに近い位置にあった。

しかし状況は一変した。AstroやNext.js、SvelteKitといった主要フレームワークが、ビルド成果物をCloudflare Workersで直接動かすアダプターを提供し始めたのである。これにより、Workerはもはや単なる中継点ではない。アプリケーションそのものがWorker上で動作する「サーバー」になった。裏側に別のオリジンサーバーは存在しなくなり、Worker自体がリクエストを処理するようになったのだ。

この変化は大きな問題を生んだ。従来のアーキテクチャでは、Workerがオリジンになると、すべてのリクエストがコードの実行を必要とするようになる。たとえ1秒前と全く同じHTMLを返す場合でも、だ。これはパフォーマンス上のレイテンシと、無視できないCPU実行コストを常に発生させることを意味していた。

静的生成と動的レンダリングのジレンマを解決する第三の道

この問題に対し、開発者はこれまで2つの選択肢から選ぶしかなかった。

  • 静的サイト生成(SSG):すべてのページをビルド時に事前生成する。表示は高速だが、コンテンツを更新するたびに全ページを再ビルドする必要がある。数千ページのサイトでは、このビルド時間が大きなボトルネックになる。
  • サーバーサイドレンダリング(SSR):リクエストのたびにページを動的に生成する。コンテンツは常に最新だが、全てのアクセスでレンダリングコストとレイテンシが発生する。

Workers Cacheはここに第三の選択肢、つまり「オンデマンドでサーバーレンダリングし、結果をキャッシュし、指定したTTL(生存期間)で更新する」という新しい手法を提供する。最初のリクエストだけがレンダリングコストを支払い、後続のリクエストはキャッシュから静的ファイルのように配信されるのだ。これはフレームワーク独自の複雑な仕組み(ISRなど)に依存しない、HTTP標準に則った解決策である。

パフォーマンスを極める主要機能「SWR」と「Vary」の内部動作

パフォーマンスを極める主要機能「SWR」と「Vary」の内部動作

stale-while-revalidate が「待ち時間ゼロ」を実現する仕組み

Workers Cacheの真価を引き出すのが、stale-while-revalidate(SWR)ディレクティブだ。これはキャッシュされたレスポンスがTTLを超過した「古い(Stale)」状態でも、とりあえずその古いデータをユーザーに返しつつ、バックグラウンドで最新のデータを取得し直すHTTPの仕組みである。

SWRがない場合、キャッシュの有効期限が切れた後の最初のリクエストは、必ずWorkerが一からページをレンダリングするまで待たされる。しかしSWRがあれば、この最初のリクエストに対しても古いキャッシュが即座に返され、ユーザーは待ち時間を感じない。Workers CacheはこのSWRを完全にサポートしており、これによって「動的なサイトなのに、まるで静的サイトのように感じる」という体験を実現している。

SWR(stale-while-revalidate)の動作イメージ
TTL 内(新鮮) Cloudflareがキャッシュから即座にレスポンスを返す。Workerは実行されない。
TTL 切れ直後(古いが許容) Cloudflareが古いキャッシュを即座に返す。同時にバックグラウンドでWorkerが起動し、最新データをキャッシュに再投入する。
キャッシュ完全消失時 初めてWorkerが起動し、ユーザーはその処理完了を待つ。ただし、これは極めて稀なケースになる。
※ SWR期間内であれば、ユーザーは常にキャッシュの速さでレスポンスを受け取れる。

この図の通り、SWRはTTLが切れた後の「最初の一人」が被る待ち時間を帳消しにする。Cloudflare Blogの記事によれば、Cloudflareは今年の早期にこのSWR機能をフルサポートしており、Workers Cacheはその上に構築されていることがわかる。

Vary ヘッダーが複数の表現をキャッシュする

現実のアプリケーションは、同じURLでもクライアントに応じて異なるレスポンスを返す必要がある。例えばブラウザにはHTMLを、APIクライアントにはJSONを返す場合や、対応状況に応じてWebPとJPEGを出し分ける場合だ。Workers Cacheは、このコンテンツネゴシエーションをHTTP標準のVaryヘッダーで解決する。

WorkerがVary: Acceptというヘッダーを付けてレスポンスを返すと、Cloudflareは「Acceptリクエストヘッダーの値」ごとに別々のキャッシュエントリを自動で作成・管理する。これにより、WebPに対応したブラウザにはWebP画像のキャッシュが、そうでない環境にはJPEG画像のキャッシュが返るようになる。開発者は複雑なキャッシュキーの設定を意識する必要はなく、標準的なHTTPのルールに従うだけで、安全かつ効率的に複数表現をキャッシュできるのだ。

開発者が知っておくべき設計思想「ゾーンではなくWorkerのキャッシュ」

開発者が知っておくべき設計思想「ゾーンではなくWorkerのキャッシュ」

エントリーポイント単位の柔軟なキャッシュ制御

Workers Cacheの最も革新的な部分は、それが「ゾーン(ドメイン)」ではなく「Worker」に紐づくという設計思想にある。この思想が、従来のCDNでは実現できなかったいくつもの高度なユースケースを可能にしている。

特に重要なのが、Workerのエントリーポイントごとにキャッシュの有効・無効を設定できる点だ。設定ファイルでエクスポート名("default""CachedBackend")を指定するだけで、認証処理を行うゲートウェイWorkerはキャッシュを無効化し(常にコードを実行するため)、その背後で重い処理を行うバックエンドWorkerだけにキャッシュを有効化する、といった構成が可能になる。

キャッシュの段階的構成(キャッシュ有効/無効の組み合わせ例)
ユーザー ゲートウェイWorker キャッシュ無効 Workers Cache(内部用) 重い処理のWorker キャッシュ有効
ゲートウェイ(キャッシュ無効) 認証やルーティング処理のため、常にコードを実行する必要がある。
バックエンド(キャッシュ有効) データベースへの問い合わせなど重い処理を含む。キャッシュがヒットすれば、処理をスキップできる。

このエントリーポイント単位の制御により、キャッシュはアプリケーションアーキテクチャの一部として自然に組み込めるようになる。単一のWorkerの中に、キャッシュするレイヤーとしないレイヤーを共存させ、それらをコードで自在に結合できるのだ。これは、CDNキャッシュを単一のオリジンの前に置くという従来の考え方とは一線を画す。

マルチテナントを安全にする ctx.props の仕組み

ユーザーごとに異なる情報を返すAPIのキャッシュは、セキュリティ上の大きな課題を伴う。ユーザーAのキャッシュがユーザーBに見えてしまうような事故は、絶対に避けなければならない。Workers Cacheはこの問題を、ctx.propsの一部を自動的にキャッシュキーに含めることで根本的に解決している。

例えば、ゲートウェイWorkerで認証したユーザーIDをctx.propsにセットし、キャッシュが有効なバックエンドWorkerを呼び出すとする。Workers Cacheはこの「ユーザーID」の違いを認識し、ユーザーごとに完全に独立したキャッシュ空間を作り出す。これにより、「認証済みAPIはキャッシュできない」という固定観念を覆し、ユーザー単位で安全にレスポンスをキャッシュできるようになる。Cloudflare Blogによれば、これは他の主要CDNでは提供されていない、Workers Cache独自の強力な利点だという。

Workers Cacheがもたらすプラットフォームとしての進化

パフォーマンスとデータの近接性を両立するアーキテクチャ

Webパフォーマンスにおいては、コードを「ユーザーの近く」で実行するのと「データの近く」で実行するのは、しばしばトレードオフの関係になる。Workers Cacheはこのジレンマに対して、キャッシュを「糊(にかわ)」として利用する解決策を提示する。

具体的には、次のような構成が現実的になる。ユーザーの近くで動き、認証やルーティングといった軽量な処理を担当するWorker Aを配置する。一方、データベースへの重いクエリやレンダリングを実行するWorker Bを、Smart Placement機能でデータの近くに配置する。Workers Cacheは、このWorker Bの手前にのみ配置する。

リクエストが来ると、Worker Aが処理した後、サービスバインディングを通じてWorker Bを呼び出す。この時、Worker Bのキャッシュがヒットすれば、データの近くにあるWorker Bは実行されることなく、ユーザーの近くにあるキャッシュからレスポンスが返る。キャッシュミス時のみ、実際のデータへのアクセスが発生する。これにより、「ユーザー近接性」と「データ近接性」の良いとこ取りが可能になるのだ。

フレームワークとの統合とコストの透明性

Workers CacheはすでにAstroフレームワークのアダプターでネイティブサポートされている。設定ファイルに数行追加するだけで、ページ単位のTTLやタグベースのキャッシュパージが利用できる。TanStack StartやNext.js(Vinext経由)など他のフレームワークへの統合も現在進行中だ。

コスト面も明快だ。Workers Cacheのキャッシュヒット時は、通常のリクエスト課金は発生するが、WorkerのCPU実行時間に対する課金はゼロになる。キャッシュストレージに対する追加のGB単位の課金もないため、コスト削減効果を予測しやすい。ダッシュボードでは、キャッシュヒット率やヒット/ミス/バイパスの内訳が確認でき、パフォーマンスチューニングに必要なデータが一元管理されている。

この記事のポイント

  • Workers Cacheは、Workerの手前に専用の階層型キャッシュを配置する新機能である。
  • これにより、サーバーサイドアプリが静的サイトのような速度を実現しつつ、CPU実行コストを削減できる。
  • stale-while-revalidateの完全サポートにより、キャッシュ更新中もユーザーを待たせない。
  • ゾーンではなくWorkerに紐づく設計により、エントリーポイント単位で柔軟なキャッシュ戦略をコードで記述できる。
  • ctx.propsをキャッシュキーに含めることで、マルチテナント環境でも安全なキャッシュが実現する。
WooCommerceクーポン自動適用、209行のコードで約1万3000行を削減

WooCommerceクーポン自動適用、209行のコードで約1万3000行を削減

WooCommerce.comは、BFCM(ブラックフライデー・サイバーマンデー)2025に向けてクーポン自動適用の仕組みを内製化し、それまで使っていたサードパーティ製プラグインを廃止した。削除したコードは実に約12,888行、新たに書いたコードはわずか209行である。WooCommerceコアのクーポン機能をそのまま活かし、再発明を避けることで、大幅なコード削減と安定性の向上を両立させた。

WooCommerce Developer Blogの記事で、開発者のRonny Shani氏がこのプロジェクトの全貌を公開した。BFCM本番では数万人規模の顧客に利用され、クーポン起因のバグはゼロだったという。返品率の低減や顧客単価の上昇といった副次効果も確認されており、少ないコードがもたらすビジネスインパクトを示す好例だ。

従来のサードパーティ製プラグイン
プラグイン 段階的割引ロジックを独自実装
プラグイン 使用制限・有効期限などを自前で再実装
プラグイン 通貨対応も独自処理
12,888行 削除対象となったコード量
内製化された自動適用プラグイン
小プラグイン 「自動適用」チェックボックスのみ追加
WooCommerceコア is_valid()で既存の検証ロジックを活用
WooCommerceコア 複数通貨対応も標準機能で動作
209行 新たに書いたコード量
削除(サードパーティ)  追加(内製化)  自動発動の指示役  検証・実行を担うWooCommerce本体

このデモでは、従来の肥大化したアプローチと内製化後のシンプルな構造を対比している。「何でも自前でやろうとする」と「コアの力を借りて指示役に徹する」の差がコード量に直結している点を視覚化した。以下、具体的な実装と成果を見ていく。

少数のコードでクーポンを自動適用する仕組み

この内製プラグインの考え方は極めて明快だ。WooCommerceがもともと持っているクーポンの検証機能(WC_Couponクラスによる使用回数制限・商品制限・有効期限チェックなど)を一切再実装せず、「いつ」「どのクーポンを」「どう適用するか」という判断部分だけを追加する。WooCommerce Developer Blogの著者Ronny Shani氏は「再発明はしない」という原則を掲げ、徹底的にコアに委ねた設計を選んだ。

このアプローチは、WordPressやWooCommerceのエコシステム全般に当てはまる教訓でもある。機能拡張が必要なとき、つい「全部入り」のプラグインを導入したり、独自のロジックを上から書いたりしがちだが、コアがすでに提供している仕組みの上に薄い層を重ねるだけで要件を満たせるケースは少なくない。コードが少なければバグの入り込む余地も減り、保守負荷も下がる。

チェックボックスひとつで制御する設計

管理画面のクーポン編集画面には「Apply coupon automatically(クーポンを自動適用する)」というチェックボックスが追加される。ここにチェックを入れると、該当クーポンの投稿メタ _auto_applyyes が保存される。判定ロジックはこのメタ値を見るだけであり、新たなデータベーステーブルや複雑な設定画面は一切作っていない。

カートが再計算されるタイミングで、プラグインは _auto_apply = yes のクーポン一覧を取得する。この一覧は12時間キャッシュされるため、WooCommerce.comのような高トラフィックサイトでもパフォーマンス上の問題は起きない。取得後は各クーポンに対して WC_Coupon::is_valid() を呼び出し、条件を満たしていれば静かに適用、満たさなくなったら静かに削除する。顧客に余計な通知を見せることもない。

再帰防止とWooCommerce.com固有の対応

実装上の唯一の「厄介なポイント」として、Shani氏は再入(re-entrancy)ガードを挙げている。クーポンを適用する処理自体が woocommerce_after_calculate_totals フックを再度発火させるため、何も対策しないと無限ループに陥る。これを防ぐために static $running フラグを導入し、処理中は再実行をブロックしている。このデバッグは、Shani氏の言葉を借りれば「なかなか楽しめた」類の不具合だったようだ。

また、WooCommerce.comの要件として、BFCMクーポンがサブスクリプション更新や特定の決済フローに適用されないようにする制御も追加されている。こうしたドメイン固有の制約はGitHub上のプルリクエストには含まれていないが、各自のストアで同様の仕組みを実装する際の参考になる。

STEP 1 カート再計算イベント発生
買い物客が商品を追加・数量変更を行うと woocommerce_after_calculate_totals が発火する
STEP 2 自動適用クーポンの取得
_auto_apply = yes のクーポンコード一覧をキャッシュから取得(12時間キャッシュ)
STEP 3 各有効性チェック
WC_Coupon::is_valid() で使用制限・有効期限・商品制限をまとめて検証。コアが処理するため再実装不要
STEP 4 自動適用または自動削除
有効なら適用・無効なら削除。いずれも顧客に通知なし。static $running フラグで再帰を防止
トリガー  取得  検証  適用・削除

上図の流れがカート再計算のたびに実行される。重要なのは、STEP 3の検証部分が完全にWooCommerceコア任せであることだ。プラグイン開発者は「どのクーポンが自動適用対象か」というメタ管理と、「適用・削除のタイミング制御」の2点だけをコード化すればよい。

BFCM 2025本番でのパフォーマンス

BFCM 2025本番でのパフォーマンス

このプラグインが初めて本格稼働したのはBFCM 2025(2025年11月19日〜12月2日)だった。結果は上々で、数万件の完了注文、数万人のユニーク顧客が3段階の割引(20%・30%・40%)を利用し、クーポン起因のバグやシステム停止は一度も発生しなかった。

WooCommerceコアのクーポン機能に乗ったことで、複数通貨対応も標準機能のまま問題なく動作した。多くのマルチカレンシーストアにとって、これは見逃せない恩恵だ。独自実装では通貨ごとの計算ロジックを自前で保守しなければならないが、コア任せならその負荷から解放される。

返品率低下と顧客単価上昇という副産物

数字にもはっきりとした改善が表れた。同記事の報告によれば、返品率は13.1%から7.8%へと約5.3ポイント低下し、顧客あたりの純現金収入は前年比25%増加した。クーポン適用の仕組みそのものが返品率に直接作用したとは考えにくいが、安定した割引適用がスムーズな購買体験につながり、結果的にポジティブな指標改善を後押しした可能性が高い。

SQLでクーポン効果を可視化する方法

同様の分析を自社ストアで行いたい場合、記事では以下のようなSQLクエリが紹介されている。クーポンコードごとに利用注文数とユニーク顧客数を集計するもので、プロモーションの効果測定に使える。

SELECT
    oi.order_item_name                  AS coupon_code,
    COUNT(DISTINCT oi.order_id)         AS orders_with_coupon,
    COUNT(DISTINCT o.customer_id)       AS unique_customers
FROM wp_woocommerce_order_items oi
JOIN wp_wc_orders o ON oi.order_id = o.id
WHERE oi.order_item_type = 'coupon'
  AND oi.order_item_name IN ('sale-20%', 'sale-30%', 'sale-40%')
  AND o.date_created_gmt BETWEEN '2025-11-19 14:00:00' AND '2025-12-02 23:59:59'
  AND o.status IN ('wc-completed', 'wc-processing')
GROUP BY oi.order_item_name
ORDER BY orders_with_coupon DESC;

クーポン名と日付範囲を自社のキャンペーンに合わせて変更すれば、同じ集計が簡単に得られる。データベースへの直接クエリになるため、実行前には必ずバックアップを取得しておきたい。

WooCommerceコアへのフィードバックと今後の展開

WooCommerceコアへのフィードバックと今後の展開

Shani氏はこの仕組みをWooCommerceのコアに取り込むためのプルリクエストをGitHub上で公開している。WooCommerce.com固有の制約は外されているが、_auto_applyメタによる自動適用のコア機能は「WooCommerceがネイティブでサポートすべき」と判断され、将来のリリースに含まれる見込みだ。

現時点でも、このプルリクエストを参考に自前のミニプラグインを構築することは十分可能である。コード量が少ないため、中級者以上のPHP開発者であれば半日もかからずに実装できるだろう。

今後に残る課題

完璧ではない部分もある。ひとつはクーポンのHPOS(High-Performance Order Storage)移行対応だ。_auto_applyメタは現在 wp_postmeta テーブルに保存されているが、注文データがHPOSに移行するタイミングでクエリの見直しが必要になる。Shani氏もこの点を「再検討が必要」として明記している。

もうひとつは、ブロックカート上でクーポン削除ボタンを非表示にするJavaScriptの実装だ。特定のブロックCSSクラス名に依存しているため、WooCommerceのバージョンアップでクラス名が変わると動作しなくなる可能性がある。本格的に汎用化するなら、より堅牢なセレクタ戦略が求められる。

また、BFCM用のクーポン名がハードコードされている点も、汎用プラグインとして配布するには改善の余地がある。現状はフィルターフックで上書きできる設計にはなっているが、管理画面から設定できるようにするほうがより実用的だろう。

「コードを書かない」判断がもたらす安定性

「コードを書かない」判断がもたらす安定性

この事例が示しているのは、技術的な巧みさよりも「何を書かないか」の判断の重要さである。WooCommerceのクーポンシステムは、利用制限、有効期限、商品カテゴリ制限、使用回数制限、複数通貨対応など、すでに十分すぎるほどの検証ロジックを備えている。それらを再実装するかわりに「適用するタイミング」だけをコード化したことで、バグの総量は劇的に減り、保守コストも最小化された。

実際の数字もこの判断の正しさを裏付けている。12,888行を削除して209行に置き換え、BFCM本番でバグゼロ。返品率は13.1%から7.8%に低下し、顧客単価は25%向上した。コードを減らすことはリスクを減らすことであり、それがそのままビジネス指標の改善に直結した好例といえる。

やりがちなアプローチ(Bad)
プラグイン 割引計算 プラグイン 有効期限チェック プラグイン 通貨換算
すべて自前で実装するためコードが膨張し、バグの温床になる
コア活用アプローチ(Good)
小プラグイン 自動適用フラグ管理 WooCommerce is_valid()で全検証
209行で完結。検証ロジックの再実装は一切なし
肥大化・自前実装  スリム・コア活用  制御役  エンジン役(コア)

この対比はWooCommerceに限らず、あらゆるシステム開発に通じる原則だ。既存の仕組みを活かし、本当に必要な差分だけをコード化する。その結果が「12,888行削除して209行追加」という数字であり、BFCM本番でのバグゼロ運用という実績である。

この記事のポイント

  • WooCommerce.comはBFCM 2025に向けてクーポン自動適用を内製化し、12,888行のサードパーティコードを209行のミニプラグインで置き換えた
  • コアの WC_Coupon::is_valid() を活用し、検証ロジックの再実装を徹底的に避ける設計が功を奏した
  • BFCM本番では数万件の注文を処理し、クーポン起因のバグはゼロ。返品率は5.3ポイント低下、顧客単価は25%向上した
  • 将来のWooCommerceコアリリースで _auto_apply メタによる自動適用がネイティブサポートされる見込み。現時点でもGitHub上のPRを参考に自前実装が可能
  • 既存の仕組みを活かして「書かない」判断を積み重ねることが、コード品質とビジネス指標の両方を引き上げる好例
WooCommerce 11.0が商品オブジェクトキャッシュを標準化!新規ストアは自動有効

WooCommerce 11.0が商品オブジェクトキャッシュを標準化!新規ストアは自動有効

WooCommerce 11.0がリリースされ、商品オブジェクトキャッシュが新規ストア向けにデフォルトで有効化された。この機能は、商品ページやチェックアウト処理で同じ商品データを何度もデータベースから取得する無駄を省き、ECサイトの表示速度を底上げする。

可変商品がある製品ページでは約9〜12%、バンドル商品を含むチェックアウト処理では6〜12%の処理速度改善が報告されている。今回の変更は、WooCommerce 10.5で実験的導入が始まったキャッシュ機構が安定動作の段階に達したことを示している。

新規にWooCommerce 11.0以上をインストールしたストアには自動で適用されるが、アップグレードした既存ストアの設定は一切変更されない。必要に応じて管理画面から手動でオンにできる仕組みだ。

商品オブジェクトキャッシュの仕組みとパフォーマンス改善の数字

商品オブジェクトキャッシュの仕組みとパフォーマンス改善の数字

リクエスト内で商品データを使い回すメモリ内キャッシュ

このキャッシュは、一度のHTTPリクエストの間だけ生きている、揮発性のメモリ内キャッシュだ。wc_get_product(123)が呼ばれると、キャッシュに保存されていればデータベースに問い合わせず、すぐに商品オブジェクトの複製を返す。リクエストが終わればキャッシュは完全に消去されるため、次のリクエストで古い情報が残る心配はない。

特に、wc_get_product()を同じIDで何度もコールするパターンが多いテーマやプラグインで効果が大きい。各呼び出しが独立したオブジェクトのクローンを返すため、意図せず商品データの状態を共有してしまうバグも防げるようになった。

キャッシュなし(従来)
1回目 wc_get_product(123) DBから取得(遅い)
2回目 wc_get_product(123) DBから再取得(まだ遅い)
3回目 wc_get_product(123) DBからまた取得
キャッシュあり(WC 11.0)
1回目 wc_get_product(123) DBから取得(初回のみ)
2回目 wc_get_product(123) メモリ内キャッシュ(即返却)
3回目 wc_get_product(123) メモリ内キャッシュ(高速)
※各呼び出しで返されるのは常に商品オブジェクトのクローン(複製)。呼び出し元どうしで状態が共有されることはない。

このデモは、同じ商品IDに対する繰り返し呼び出しがキャッシュによってどれだけ無駄を省けるかを示している。実際のECサイトでは、商品ループやセール情報の取得、決済フローの中で wc_get_product() が頻繁に呼ばれるため、体感速度の改善が期待できる。

実測値として9〜12%の高速化

WooCommerce 10.5の実験的導入時に公表されたパフォーマンス測定値は、今回の正式適用後も同じだ。可変商品(サイズや色のバリエーションがある商品)の製品ページでは、読み込み時間が9〜12%短縮された。バンドル商品を含むチェックアウト処理では6〜12%高速化されている。

この改善幅は、商品データを繰り返し取得する重いクエリがページ表示のボトルネックになっている状況で顕著だ。小規模なサイトでは効果が体感しにくいケースもあるが、商品数が多いストアや複雑な製品構成のサイトでは、目に見える速度向上が得られる。

WooCommerce 10.5から11.0への段階的な導入プロセス

WooCommerce 10.5から11.0への段階的な導入プロセス

10.5で実験的機能として登場、10.6で互換性の宣言が「互換」に

商品オブジェクトキャッシュは、WooCommerce 10.5で「実験的機能」として初めて公開された。当時はデフォルトで無効であり、ストア運営者が手動でオンにする必要があった。機能自体は、wc_get_product()の呼び出しをインターセプトし、リクエスト単位のメモリ内キャッシュからデータを返す仕組みだ。

WooCommerce 10.6では、この機能のプラグイン互換性宣言が「非互換」から「互換」に変更された。実験期間中に拡張機能エコシステム全体で互換性の問題が一切報告されなかったため、明示的に互換性を宣言していない拡張機能も、デフォルトで互換とみなされるようになった。

もし拡張機能が明示的に「この機能とは非互換」と宣言している場合は、WooCommerceの機能画面に従来通り非互換の通知が表示される。しかし、そのような宣言が必要になった拡張機能は見つかっていない。

11.0で新規ストアへの自動有効化を実装

WooCommerce 11.0のクリーンインストール時には、インストールプロセスの一環として商品オブジェクトキャッシュが自動で有効になる。機能登録自体は enabled_by_default => false のまま変更されていないが、新規インストール時に明示的に有効化が書き込まれる仕組みをとっている。

これは、HPOS(High-Performance Order Storage)など、他の機能がオプトインから新規インストール時デフォルトへ移行した際と同じパターンだ。バージョンアップだけでは既存ストアの設定を変更しない、慎重な設計が貫かれている。

既存ストアの挙動と手動有効化の手順

既存ストアの挙動と手動有効化の手順

アップグレードしても設定は一切変わらない

WooCommerce 11.0より前に構築されたストアは、11.0へアップグレードしても既存のキャッシュ設定がそのまま維持される。もし以前に無効だった(それが11.0以前のストアのデフォルト設定だ)なら、アップグレード後も無効のままだ。すでに手動で有効にしていたストアは、そのまま有効が継続される。

つまり、次の3パターンに整理できる。

  • WooCommerce 11.0以降を新規インストール → 自動的に有効(操作不要)
  • 既存ストアで機能が無効だった → アップグレード後も無効のまま(手動で有効にできる)
  • 既存ストアで機能が有効だった → アップグレード後も有効のまま(操作不要)

手動で有効化する方法

既存ストアでこの機能を試したい場合、管理画面の WooCommerce → 設定 → 詳細設定 → 機能 にアクセスし、「商品オブジェクトをキャッシュする(Cache Product Objects)」のトグルをオンにすればよい。ただし、設定変更後にサイト全体の動作を一通りチェックしておくことが推奨される。

手動有効化の流れ
STEP 1 WooCommerce → 設定 へ移動
STEP 2 詳細設定 → 機能タブを開く
STEP 3 「Cache Product Objects」をオンにする
STEP 4 変更を保存して動作確認

トグルをオンにすると、その瞬間からリクエスト単位の商品キャッシュが働き始める。とくに、複数の拡張機能が同じ商品データにアクセスする大規模ストアでは、即効性のある改善が期待できるだろう。

拡張機能開発者への影響と注意点

拡張機能開発者への影響と注意点

標準APIを使っていればコード変更は不要

wc_get_product() やその他 WooCommerce 標準 API を通じて商品を取得している拡張機能は、このキャッシュの影響をまったく受けない。キャッシュは内部で透過的に動作し、呼び出し元には変わらず正しい商品オブジェクトが返る。

キャッシュから返されるのは、あくまで独立したクローンなので、取得した商品オブジェクトを編集しても他の処理に副作用が及ぶことはない。したがって、従来のコーディング規約に従っている拡張機能は、そのまま動作し続ける。

直接SQLを実行する拡張機能が注意すべきポイント

注意が必要なのは、WooCommerceのメタフックを経由せずに、商品データに対して直接SQLクエリを発行している拡張機能だ。これらのクエリはキャッシュの無効化フックを迂回するため、更新後のデータがキャッシュに反映されず、古い情報が返ってしまう可能性がある。

この問題は、商品オブジェクトキャッシュに限った話ではなく、WooCommerceのデータ整合性全般に関わる設計課題だ。該当する拡張機能を開発している場合は、標準の wc_get_product() やメタデータ更新用のAPIを使うことで、キャッシュの恩恵を受けつつ、データ不整合も回避できる。

今後のロードマップと全ストアへの有効化計画

今後のロードマップと全ストアへの有効化計画

データ蓄積後に全ストアで有効化へ

今回のリリースは通過点であり、最終的にはすべてのストア(既存ストアも含む)で商品オブジェクトキャッシュを有効化する計画が示されている。現時点で具体的な実施時期は明言されていないが、十分なストアでの稼働データが蓄積された段階で判断される。

すでに実験期間で問題が報告されていないこと、新規インストールのデフォルト有効化でさらなる実績が積み上がることを踏まえると、早ければ数バージョン後には全ユーザー対象の強制有効化に踏み切る可能性が高い。

問題が発生した場合の報告先

もし商品オブジェクトキャッシュに関連して予期せぬ挙動が見つかった場合、WooCommerceのGitHubリポジトリでIssueを報告してほしいと開発チームは呼びかけている。拡張機能開発者やストア運営者からのフィードバックが、今後の安定化に直結する。

この記事のポイント

  • WooCommerce 11.0 で商品オブジェクトキャッシュが新規ストア向けに自動有効化された
  • 可変商品の製品ページ読み込みは 9〜12% 高速化、バンドル商品のチェックアウトは 6〜12% 改善
  • 既存ストアはアップグレードしても設定変更なし、管理画面から手動でオンにできる
  • 標準の WooCommerce API を使う拡張機能はコード変更不要で動作する
  • 最終的には全ストアで有効化される予定で、Issue 報告を募っている
Action Scheduler 4.0.0の変更点、WooCommerceのテーブル肥大化を抑制

Action Scheduler 4.0.0の変更点、WooCommerceのテーブル肥大化を抑制

WooCommerceの裏側で動くAction Schedulerは、多くのデータベースの中でも特に負荷の高いテーブルを持つ。高トラフィックのストアでは、完了した処理を削除する仕組みが追いつかず、失敗したアクションは一切消えないまま蓄積し続けることが問題になっていた。4.0.0はその根本に手を入れたメジャーアップデートだ。失敗アクションの保持期間をデフォルトで3か月に制限し、クリーンアップを専用のデイリージョブとして分離した。これにより、アクションとログのテーブルサイズが際限なく肥大化する状態を防げる。

本バージョンは7月28日リリース予定のWooCommerce 11.0にバンドルされ、すでにWordPress.orgで単独でも入手可能だ。互換性を壊す変更が複数含まれているため、拡張機能を開発している人や大規模ストアを運用している人は、4.0.0での動作検証を早めに始める必要がある。

4.0.0が狙う根本的なテーブル肥大化の抑制

4.0.0が狙う根本的なテーブル肥大化の抑制

Action SchedulerはWordPress管理画面での注文処理やメール送信など、WooCommerceの非同期ジョブを支えるコアライブラリだ。これまでは小さなバグフィックスが中心で、3.9.x台を刻んでいた。しかし今回、互換性を壊す複数の変更をまとめて投入するため、バージョン番号が4.0.0にジャンプした。WordPress形式のバージョン付けでは3.9.3の次は3.10ではなく4.0だから、意図的な動きといえる。

従来のクリーンアップ(Before)
アクションテーブル 完了・キャンセルのみ削除
失敗アクション 無期限で保持
クリーンアップ キュー処理のついでに小分け
4.0.0のクリーンアップ(After)
失敗アクション 3か月後に自動削除
専用ジョブ 毎日3時に一括処理
バッチサイズ 最低250件、最大まで連続

このデモが示すように、クリーンアップの仕組みが根本から見直された。特に失敗アクションが自動削除の対象になった点と、削除処理が専用ジョブとして分離された点が、テーブル肥大化を抑える大きな柱だ。

互換性の壁を越えるメジャーバージョンアップ

4.0.0ではWordPress 6.8以上の動作要件が課せられ、WordPress 7.0との互換性も明示された。これは今後のWooCommerceエコシステムにとって、基盤環境を一段上げる布石でもある。また、後述するユニークアクションの判定変更は、同じフックでも引数が異なれば別物として生成されるようになり、既存コードの重複防止ロジックに影響を与える可能性がある。

失敗アクションの保持期間を3か月に制限

失敗アクションの保持期間を3か月に制限

これまでAction Schedulerは、完了とキャンセルのアクションだけを削除していた。失敗ステータスのアクションは、自らフィルターで追加しない限り永久に残り続けた。多忙なストアではこれが原因でアクションテーブルとログテーブルが無制限に成長し、自力で回復できない状況に陥っていた。4.0.0では、失敗アクションが発生から3か月を超えると自動的に削除される専用のクリーンアップパスがデフォルトで有効化された。

保持期間の変更(変更前)
失敗したアクションは永久保持
テーブルが成長し続け、手動削除が必要
保持期間の変更(4.0.0)
失敗後3か月で自動削除
四半期決算サイクルと整合し、調査猶予も確保
カスタマイズ例
保持期間を6か月に延長したり、削除そのものを無効化することも可能。

3か月という期間は、典型的な四半期会計サイクルに合わせつつ、障害調査のための十分な猶予を残す設計だ。より厳格なデータ保持ポリシーを持つストアでは、action_scheduler_retention_period_for_failedフィルターで秒単位の期間を変更できる。あるいはaction_scheduler_enable_failed_action_cleanup__return_falseを渡せば、4.0.0以前と同じく無期限保持に戻せる。

注目すべき点は、既にaction_scheduler_default_cleaner_statusesフィルターで失敗ステータスを追加していた場合、そちらの設定が優先されることだ。その場合は、4.0.0の新しい失敗専用パスではなく、既存のクリーンアップサイクルに統合されるため、動作が変わることはない。

クリーンアップを専用のデイリージョブに分離

クリーンアップを専用のデイリージョブに分離

旧バージョンでは、古いアクションの削除はキューの各バッチ処理にインラインで埋め込まれ、一度に少量しか処理されなかった。そのため、処理量の多いストアではクリーンアップが追いつかず、テーブルが大きくなる一方だった。4.0.0では、クリーンアップを独立したタスクとし、サイト時刻で毎日午前3時に一度だけ実行する方式に変更された。

クリーンアップ実行の流れ
STEP 1 毎日午前3時に専用ジョブが起動
STEP 2 一度に最低250件の削除を実行
STEP 3 バックログがなくなるまで再スケジュール

この方式により、削除処理が通常のキュー処理のパフォーマンスに影響を与えなくなり、大規模テーブルでも遅延なく追いつけるようになった。バッチサイズはaction_scheduler_cleanup_batch_sizeフィルターで変更可能で、デフォルトの250件より少なくも多くもできる。もし従来のインライン方式に戻したい場合は、カスタムキュークリーナーを実装すれば自動的にそちらが使われるが、ほとんどのサイトではその必要はないだろう。

ユニークアクションの判定に引数が加わった

ユニークアクションの判定に引数が加わった

as_enqueue_async_action()やスケジュール系関数の$uniqueパラメータは、同じアクションが重複して生成されるのを防ぐためのものだ。従来はフック名とグループだけを比較していたため、引数が異なる2つのアクションでも同一とみなされ、後のほうが黙って破棄される挙動だった。これが4.0.0では、引数の内容まで含めて同一性を判定するように変更された。

従来の重複チェック(Before)
フック名 + グループ のみ比較
引数が違うアクションも重複とみなされる
4.0.0の重複チェック(After)
フック名 + グループ + 引数 を比較
引数が異なれば別のアクションとして生成

この変更は互換性を壊すため、特に注意が必要だ。旧来のフックとグループだけの重複防止に依存していたコードでは、これまでよりも多くのアクションが生成されるようになる。意図しない大量のジョブがキューに積まれないよう、$uniqueを使っている箇所は必ず見直してほしい。

WooCommerceサイトへの実務的な影響と移行のポイント

WooCommerceサイトへの実務的な影響と移行のポイント

4.0.0はWooCommerce 11.0のバンドルに先立って単独テストが可能だ。大規模ストアや独自の拡張機能でAction Schedulerを利用している開発者は、以下の3点を中心にステージング環境で動作検証を行うことを推奨する。

  • 失敗アクションの保持ポリシー
    3か月のデフォルトが自社のデータ保持要件に合致するか確認し、必要ならフィルターで調整する。
  • ユニークアクションの重複防止ロジック
    $unique=trueを使用している全箇所を洗い出し、引数が異なるアクションが正しく生成されるかテストする。
  • クリーンアップの実行タイミング
    デイリージョブへの移行により、削除がバッチ処理から外れたことで、期待していたリアルタイム性が失われていないか確認する。必要に応じてカスタムクリーナーを実装する。

開発元のWooCommerceチームはGitHubでフィードバックを募集しており、予期しない動作があれば早期に報告するよう呼びかけている。WooCommerce 11.0の正式リリースまで1か月あまり。致命的なトラブルを回避するために、今のうちに4.0.0との互換性テストを済ませておくことが賢明だ。

この記事のポイント

  • Action Scheduler 4.0.0はテーブル肥大化を防ぐため、クリーンアップの仕組みを根本から見直したメジャーアップデート
  • 失敗アクションがデフォルトで3か月後に自動削除されるようになり、保持期間のカスタマイズも可能
  • クリーンアップが専用のデイリージョブとして実行され、キュー処理のパフォーマンスに影響しなくなった
  • ユニークアクションの重複チェックに引数が含まれるようになり、既存の重複防止ロジックへの影響に注意が必要
  • WooCommerce 11.0へのバンドル前に単体テストを行い、互換性の問題を早期に発見することが重要
PostgreSQLで大規模削除をスケールさせるならDROP TABLE一択

PostgreSQLで大規模削除をスケールさせるならDROP TABLE一択

PostgreSQLでテーブルから大量の行を削除する必要に迫られたとき、DELETE文をそのまま使うのは最悪の選択肢のひとつだ。一見直感的ではないが、大規模なDELETEはデータベースに余計な仕事を追加するだけに終わる。

一方で、DROP TABLEやTRUNCATEはテーブルごと削除することで、デッドタプルやバキュームといった負債を生まず、即座にディスク領域を開放する。この記事では、なぜDELETEがスケールしないのか、そしてDROP TABLEがなぜ高速なのかをMVCCや物理ストレージの観点から解説する。

さらに、大量の不要データが混入したテーブルを安全にクリーンアップする実践的な手法や、日常的な削除処理をパーティショニングでDROPに変える設計術も紹介する。

なぜDELETEはスケールしないのか

なぜDELETEはスケールしないのか
DELETEのデータ処理フロー(非効率)
大量のDELETE文を発行
Postgresがデッドタプルを生成
テーブルとインデックスに無効データが蓄積
バキュームがデッドタプルを回収(すぐに完了せず)
ディスク領域はOSに返還されない(再利用可能な状態)
読み取りクエリがデッドタプルをスキップするオーバーヘッド発生
レプリケーションにも書き込みが発生し、他トランザクションに影響
🔁 vs
DROP TABLE / TRUNCATEのデータ処理フロー(効率的)
DROP TABLE / TRUNCATE を実行
アクセス排他ロックを取得(一瞬〜短時間)
テーブルファイルをOSから直接削除
共有バッファキャッシュ内の該当ページのメタデータをスイープ
デッドタプルゼロ、バキューム不要
即座にディスク領域がOSに返還される

このデモはDELETEとDROP TABLEのデータ処理フローの違いを示している。DELETEはデッドタプルとバキュームという負債を生み、領域をOSに返さない。DROP TABLEはファイル削除だけで完了する。

MVCCとデッドタプルの正体

PostgreSQLは行が更新されるたびに、元の行を「古いバージョン」として内部に保持する。これはMVCC(Multi-Version Concurrency Control / マルチバージョン同時実行制御)と呼ばれ、異なるトランザクションがそれぞれの時点のデータを正しく読み取れるようにする仕組みだ。

この設計では、DELETE文を実行しても行が物理的に即座に消えるわけではない。削除された行は「デッドタプル」としてテーブルやインデックスに残り続ける。後にバキューム処理がそれらを回収して領域を再利用可能にするが、その間も読み取りクエリはデッドタプルをスキップするためのオーバーヘッドを負う。

さらに、通常のバキュームや自動バキューム(autovacuum)は、デッドタプルが占めていたページを「書き込み可能」とマークするだけで、OSにディスク領域を返還しない。PostgreSQLはINSERTとDELETEが混在するワークロードで領域を再利用しやすいようにこの挙動を選んでいる。OSへの領域返還にはVACUUM FULLが必要だが、長時間の強力なロックを伴う。

レプリケーションとバキュームの重み

DELETEは書き込み操作としてWAL(Write Ahead Log)に記録され、レプリカにも転送される。同期レプリケーション環境では、大量のDELETEがコミットされるまで他の書き込みトランザクションが待たされる可能性がある。つまり、DELETEは「それ自体が負荷を増やす」のであり、後片付けもバキュームに丸投げする形になる。

インデックスに関しても、DELETE実行時にインデックスのエントリは即座に消されない。読み取り時に「このタプルは無効か」を逐一判定する必要があり、インデックススキャンがデッドタプルを見つけた場合、ベストエフォートでそのエントリを無効化する最適化はあるものの、根本的なオーバーヘッドは残る。

DROP TABLE/TRUNCATEが高速な理由

DROP TABLE/TRUNCATEが高速な理由

DROP TABLEとTRUNCATEはテーブルに対してAccessExclusiveLock(アクセス排他ロック)を取得するため、他のトランザクションがそのテーブルを読み書きできなくなる。しかし、処理そのものはデータ量にほぼ依存しない。内部的にはテーブルに関連する物理ファイルをOSから直接削除し、共有バッファキャッシュからも該当ページのメタデータを一掃する。

PostgreSQLの共有バッファは8KBのページ単位で管理され、各ページに64バイトの固定サイズのヘッダが付与される。テーブル削除時にスキャンするのはページ本体ではなく、このヘッダ情報のみだ。例えば128GBの共有バッファがあっても、スキャンするメタデータは全体の1/128の約1GBに過ぎず、シーケンシャルアクセスで高速に処理できる。これがデータサイズに依存しない真の理由である。

一時的な大量削除への実践アプローチ

一時的な大量削除への実践アプローチ

テンポラリテーブルを使った外科手術

バグによってテーブルに大量の不要データが混入したケースを考えよう。保持すべきデータは数十万行程度で、残りはすべて削除対象だ。ダウンタイムが数分許容できるなら、以下の手順で一気にクリーンアップできる。

-- 1. 対象テーブルを排他ロック
LOCK TABLE big_table IN ACCESS EXCLUSIVE MODE;

-- 2. テンポラリテーブルに保持したいデータだけコピー
CREATE TEMP TABLE temp_keep_big_table AS
  SELECT * FROM big_table
  WHERE updated_at >= '2026-04-01';

-- 3. 元テーブルをTRUNCATE
TRUNCATE big_table;

-- 4. テンポラリテーブルからデータを再挿入
INSERT INTO big_table SELECT * FROM temp_keep_big_table;

この手順ではテーブルを完全にロックするため、オンラインサービスではダウンタイムが発生する。しかし、ロック時間が分単位で許容できるメンテナンスウィンドウがあるなら、数十万行のテーブルでも数分で処理できる。実際にPlanetScale社内のオブザーバビリティツールで同様のケースが発生し、この手法で問題を解決している。WALに書き込まれるのは、4の再挿入で戻された行だけであり、DELETEによる膨大なログは一切発生しない。

トリガーを使ったゼロダウンタイムの切り替え

より高度な手法として、テーブルへの書き込みを新しいテーブルにミラーリングし、タイミングを見計らってアトミックなリネームで切り替える方法がある。具体的には、元のテーブルにトリガーを設定して、INSERTやUPDATE、DELETEを新テーブルにも反映させる。十分にデータが同期された段階で、短時間の排他ロックを取得し、テーブルをリネームして差し替える。

このアプローチはPostgreSQLの拡張であるpg_squeeze(pg_repackの後継)が行っていることと本質的に同じだ。ただし、pg_squeezeは既に肥大化したテーブルを最適化するためのツールであり、この記事で伝えたいのは「設計段階で大規模DELETEを避けておく」ことである。初めからスキーマをコントロールできれば、こうした事後対応は不要になる。

パーティショニングで日常的な削除をDROPに置き換える

パーティショニングで日常的な削除をDROPに置き換える
親テーブル events
2026-06-11 → 子テーブル events_20260611
2026-06-10 → 子テーブル events_20260610
2026-05-01 → 子テーブル events_20260501
定期的なジョブで古い子テーブルをDROP
DROP後の状態
events_20260501 が削除され、関連ファイルが即座に解放
デッドタプルなし、バキューム不要

このデモは日付パーティションを使ったエージングアウトと、DROP TABLEによる高速な領域解放の流れを示している。

PostgreSQL 10以降、パーティショニング機能が大幅に強化された。親テーブルの背後に子テーブルを複数持ち、クエリは自動的に該当の子テーブルに振り分けられる。日付ベースのRANGEパーティションを使えば、過去のデータを保持する子テーブルを定期的にDROP TABLEするだけで、古いデータを一瞬で削除できる。これは、数百万行単位のDELETEを定常的に実行していたワークロードを、数秒のDROP TABLEに置き換える強力なテクニックだ。

pg_partman拡張を利用すれば、子テーブルの自動作成や古いパーティションの削除をスケジュール実行できる。また、パーティショニングは再帰的に構成できるため、より高度な設計も可能だ。たとえば、最上位をLISTパーティションで「可視行」と「不可視行」に分け、「不可視行」の子テーブルをさらにRANGEパーティションで日付ごとにエージアウトさせる、といった多次元の構成が組める。

スキーマ設計でDELETEをDROPに置き換える視点

スキーマ設計でDELETEをDROPに置き換える視点

大量データを削除する必要が生じるアプリケーションでは、テーブル設計の段階からDELETEをDROPやTRUNCATEで代替できるか検討することが重要だ。DELETEを多用しない設計にすることで、読み取りクエリのレイテンシ低減、レプリケーションラグの抑制、バキューム負荷の軽減といった効果が期待できる。

パーティショニングしかり、トリガーベースのテーブル差し替えしかり、選択肢は多様だ。PostgreSQLのMVCCが持つ根本的な制約を理解し、大規模な行削除は「テーブルごと破棄して必要なデータだけを再構築する」という発想でスキーマを組み立てる。その結果、データベースの健全性は飛躍的に向上する。

この記事のポイント

  • DELETEはデッドタプルを生成し、バキュームやレプリケーションに余計な負荷をかける。大規模削除には向かない
  • DROP TABLEやTRUNCATEはデータ量に依存せず、物理ファイルの削除とバッファキャッシュのメタデータスイープで瞬時に領域を解放する
  • 一度きりの大量削除はテンポラリテーブルとTRUNCATEの組み合わせが有効。ダウンタイムを許容できるなら強力な手法
  • 定常的な古いデータの削除には、パーティショニングでDROP TABLEに置き換える設計が有効。日付パーティションとpg_partman拡張で自動化できる
  • アプリケーション設計時に「大量削除が必要なテーブル」をDROPできるようスキーマを工夫することで、データベースの健全性を大幅に向上できる
AWS Graviton5搭載EC2 M9g/M9gdがGA。汎用インスタンス性能限界突破の全容

AWS Graviton5搭載EC2 M9g/M9gdがGA。汎用インスタンス性能限界突破の全容

AWSが2026年6月10日、Graviton5プロセッサを搭載したEC2 M9gおよびM9gdインスタンスの一般提供を開始した。Armアーキテクチャベースの第5世代カスタムシリコンであり、前世代比で最大25%の計算性能向上を実現したとされている。

2025年末のプレビュー公開から半年、ClickHouseやHoneycombといった企業が実運用環境で検証を重ね、コード変更ゼロで36%の性能向上を確認している。HubSpotではMySQLデータベースのクエリ処理時間が最大60%短縮されたとの報告もある。

Arm系インスタンスはこれまでも存在したが、192コア、5倍のL3キャッシュ、DDR5-8800対応メモリを搭載したGraviton5は次元が異なる。本記事ではM9g/M9gdの技術的進化と、それがビジネスにどう影響するかを具体的に解説する。

Graviton5とは何か。5世代の進化がもたらしたもの

AWSのGravitonプロセッサは、Armアーキテクチャを採用したAWS独自設計のカスタムシリコンだ。第1世代が登場したのは2018年。以来8年にわたり継続的に投資が続けられ、現在では350以上のインスタンスタイプがGravitonで稼働している。

Arm系クラウドインスタンスの現在地

Armアーキテクチャとは、スマートフォンやタブレットで広く使われている省電力設計のCPU命令セットだ。これに対し、従来のサーバCPUの多くはx86アーキテクチャ(IntelやAMDが採用)で動作していた。Armは消費電力あたりの処理効率に優れており、クラウドの大規模データセンターで電気代を抑えつつ高性能を発揮できる点が評価されている。

AWS広報情報によれば、現在12万以上の顧客がGravitonを採用。スタートアップから大企業まで幅広く、Webアプリケーション、マイクロサービス、データベース、機械学習推論、ゲームサーバ、動画エンコーディングなど多様な用途で使われている。x86依存の強い従来のクラウド常識を、Armが着実に塗り替えつつある。

従来のクラウド選択肢(5年前)
x86系 Intel / AMD がほぼ独占
※Armは選択肢として存在せず
現在のクラウド選択肢(Graviton5登場後)
x86系 従来通り利用可
Arm系 Graviton5 で性能・省電力両立

クラウドインスタンスの選択肢は、この5年で一変した。Armはもはや「実験的な選択肢」ではなく、x86と並ぶ本流の一つとして位置づけられる。特にGraviton5では、その傾向がさらに加速するだろう。

Graviton5が前世代から飛躍した3つの要素

Graviton5の改良点を、AWS公式発表から整理する。最も注目すべきは次の3つだ。

  • 計算性能の大幅向上:Graviton4比で最大25%の計算性能向上。Webアプリケーションで最大35%、機械学習推論で最大35%、データベースで最大30%の高速化が実測されている
  • 5倍のL3キャッシュ:CPUが頻繁にアクセスするデータを一時保存する高速メモリ領域が前世代比5倍に拡大。コア間のデータ待ち時間が最大33%削減された
  • DDR5-8800メモリとPCIe Gen6対応:クラウド上のプロセッサインスタンスとして最速水準のメモリ帯域幅を実現。PCIe Gen6はGen5比でデータ転送速度が2倍となり、NVMeストレージや高速ネットワークとの連携性能が飛躍的に伸びる

L3キャッシュの増量は、単なる数値スペックの向上ではない。CPUは計算のたびにメインメモリまでデータを取りに行くと時間がかかる。L3キャッシュが大きければ近くにデータを置けるため、処理待ちが減り、結果として体感性能が大きく向上する仕組みだ。

実際にAWSの広報記事で紹介された顧客事例では、ClickHouseがコード変更なしでM8g比36%の性能向上を達成。Honeycombは6カ月にわたるA/Bテストで、コアあたりのスループットが36%向上したと報告している。これらの数字は、CPUそのものの改良がアプリケーションレベルで直接的な効果を生むことを示している。

M9g/M9gdのラインアップと性能スペック

インスタンスサイズと性能の詳細

M9gは汎用用途向けで、1vCPUあたり4GiBのメモリ比率を採用している。M9gdはこれに加え、高速ローカルNVMe SSDストレージを搭載したバリエーションだ。ラインアップは1vCPUの小規模構成から、192vCPU・768GiBメモリの大規模構成まで幅広く用意されている。

M9g 汎用タイプ
1〜192 vCPU 4〜768 GiB RAM 最大100 Gbps NW
Webアプリ、マイクロサービス、コンテナ、Java大規模アプリに好適
M9gd ローカルNVMe搭載タイプ
1〜192 vCPU 59GB〜11.4TB SSD IOPS 30%向上
キャッシュ、メディア処理、バッチ処理、一時ストレージ用途に好適

最大サイズの48xlarge(192vCPU)では、ネットワーク帯域が100Gbpsに達する。前世代比で最大2倍の帯域幅になっており、大量のデータを扱うデータベースやログ処理基盤での効果が特に大きい。

IBC(Instance Bandwidth Configuration)の実用性

M9g/M9gdでは、IBC(インスタンス帯域幅設定)と呼ばれる新機能が利用可能になった。これはEBS(永続ストレージ)とVPCネットワーク間で、帯域幅の配分を最大25%調整できる仕組みだ。

IBC未使用時(デフォルト配分)
EBS帯域 50%
データベース書き込み速度が制限される
VPC帯域 50%
ネットワーク通信には十分
IBC使用時(DB重視に調整、最大25%シフト)
EBS帯域 62.5%
データベース書き込みが高速化
VPC帯域 37.5%
ネットワーク通信には依然十分

たとえばデータベースサーバではEBSへの書き込み性能がボトルネックになりやすい。IBCを使えばEBS側に帯域を多めに割り当て、クエリ処理やログ書き込みを高速化できる。ネットワーク通信が少ないバッチ処理やキャッシュサーバでも有効だ。

Nitro Isolation Engineが実現する「数学的に証明されたセキュリティ」

Graviton5と同時に発表された技術の中で、最も静かでありながら最も革新的なものがNitro Isolation Engineだ。聞き慣れない用語だが、クラウドセキュリティの考え方を根本から変える可能性がある。

形式検証(Formal Verification)とは何か

通常、ソフトウェアのセキュリティは「テスト」で検証する。攻撃パターンを想定し、実際に動かして問題がないかを確認する手法だ。しかしこの方法では、想定外の攻撃や未知の脆弱性を見逃すリスクが常に残る。

形式検証(Formal Verification)はこれとは根本的に異なる。数学の定理証明と同じアプローチで、「このシステムは絶対に想定外の動作をしない」ことを数理的に証明する技術だ。特定のテストケースだけでなく、あらゆる入力パターンで期待通りに動作することを保証する。

AWSによれば、Nitro Isolation Engineはこの形式検証を適用したクラウドハイパーバイザーとして業界初の事例となる。ハイパーバイザーとは、1台の物理サーバ上で複数の仮想マシンを安全に隔離する基盤ソフトウェアだ。この隔離機能が破られると、他の顧客のデータにアクセスされる重大なセキュリティ事故につながる。Nitro Isolation Engineは、その隔離が破られる可能性を数学的にゼロにする設計となっている。

従来のセキュリティ検証 対 形式検証
従来のテストベース検証
「考えられる攻撃」を列挙し、それらが失敗することを確認。想定外の攻撃は見逃す可能性あり
形式検証(Nitro Isolation Engine)
数学的に「あらゆる入力・あらゆる状況で隔離が破れない」ことを証明。未知の攻撃にも原理的に耐性

この技術は金融機関や医療機関など、厳格なデータ保護が求められる業界にとって特に重要な意味を持つ。セキュリティ監査のレベルが一段引き上げられることになるからだ。なおNitro Isolation EngineはM9g/M9gd専用の機能であり、既存のインスタンスタイプには搭載されない。

エージェントAI時代のCPU需要とGraviton5の位置づけ

AIが「考える」から「行動する」へのシフト

ここ数年、AIの進化は大規模言語モデル(LLM)のテキスト生成能力に注目が集まってきた。しかし現在、AIの主戦場は「質問に答える」から「行動を実行する」へと急速に移行している。いわゆるエージェントAIと呼ばれる分野だ。

エージェントAIとは、ユーザーの指示に対して、コードを実行し、ツールを使い、結果を評価し、複数ステップのタスクを自律的に組み立てるAIシステムを指す。たとえば「今月の売上データを分析してグラフ化し、経営陣向けのサマリをSlackに投稿して」という指示に対し、AIがデータベースに接続し、集計処理を実行し、グラフを生成し、メッセージを送信する一連の流れを自律的に処理する。

このような処理は、GPUなどのアクセラレータだけで完結しない。指示の解釈、コードのコンパイル、データベースクエリの実行、APIの呼び出しなど、CPUに依存する処理が大量に発生する。AWSの広報記事で、MetaがエージェントAI基盤として数千万コア規模のGravitonを導入していると報告されているのは、このトレンドを象徴している。

エージェントAIの処理フローとCPU需要
STEP 1 ユーザー指示の解釈
自然言語の解析、意図の抽出。CPUが実行
STEP 2 コード生成と実行
PythonやSQLのコードを生成し、実際に実行。CPU負荷が高い
STEP 3 ツール操作と結果評価
API呼び出し、データベース接続、結果の検証。並列処理が発生

エージェントAIが実用段階に入るにつれ、クラウド上のCPU需要はむしろ増大する。Graviton5が192コアという高密度設計を採用したのは、こうした並列処理ニーズを先取りしたものといえる。

Web開発者にとっての実務的意味

中小企業のWeb担当者や個人事業主にとって、「エージェントAI」や「192コア」という言葉は遠い世界に感じられるかもしれない。しかし実際には、以下のような形でM9gの恩恵は身近な領域に及ぶ。

  • MySQL/PostgreSQLの応答速度向上:HubSpotの事例ではクエリ時間が最大60%短縮。WordPressサイトやECサイトのデータベース応答が高速化する可能性がある
  • コスト効率の改善:Graviton5はGraviton4比でエネルギー効率も向上。同じ処理をより少ない電力で実行できるため、ランニングコストの削減につながる
  • セキュリティの底上げ:Nitro Isolation Engineによる隔離保証は、顧客データを扱うあらゆるサービスに恩恵がある

重要なのは、これらの恩恵がコード変更ゼロで得られるケースが多い点だ。ClickHouseやHoneycombの報告にあるように、Armネイティブ対応が済んでいるアプリケーションであれば、インスタンスタイプをM8gからM9gに変更するだけで性能向上が見込める。

M9g/M9gdへの移行を検討する際の実践ステップ

Graviton5インスタンスの利用を始めるには、いくつかの準備と確認が必要だ。AWS公式が提供する移行ガイドやツールを活用すれば、想定よりスムーズに移行できる。

Arm対応状況の確認と移行パス

最初に行うべきは、現在稼働中のアプリケーションがArmアーキテクチャに対応しているかの確認だ。Java、Python、Node.js、Go、PHPなど主要な言語ランタイムはすでにArm対応が完了している。ただし、x86固有のアセンブリコードを含むC/C++プログラムや、特定のx86向けバイナリに依存しているアプリケーションでは注意が必要になる。

Graviton移行の3ステップ
ステップ1:対応状況の確認
言語ランタイム、依存ライブラリ、DockerイメージがArm対応か確認。AWS公式のGetting Started Guideを参照
ステップ2:テスト環境での検証
小規模なM9gインスタンスでワークロードを試験運用。性能と安定性を確認
ステップ3:本番移行とコスト最適化
Savings Plansの活用、Graviton Savings Dashboardでのコスト効果測定を並行実施

Javaアプリケーションの場合、AWSが提供する「AWS Transform」というAI支援サービスが利用できる。x86用にコンパイルされたJavaアプリケーションをArm向けに自動変換し、互換性分析や依存関係の更新まで処理するツールだ。コードの書き換えが必要なケースでも、変換作業の多くを自動化できる。

コスト面の評価ポイント

M9g/M9gdは、Savings Plans、オンデマンド、スポットインスタンス、Dedicated Hostsのいずれでも購入可能だ。一般にGraviton系インスタンスはx86系より低価格に設定されており、さらにSavings Plansを組み合わせることで長期利用時のコストを大幅に抑えられる。

AWS公式が提供する「Graviton Savings Dashboard」を使えば、Graviton移行によるコスト削減効果を可視化できる。費用対効果を数字で把握しながら、段階的に移行を進めるのが実務的なアプローチだ。

この記事のポイント

  • AWS Graviton5搭載M9g/M9gdが一般提供開始。前世代Graviton4比で最大25%の計算性能向上
  • ClickHouseで36%、HubSpotのMySQLクエリで最大60%の高速化を実測。コード変更不要のケースが多い
  • Nitro Isolation Engineにより、形式検証を用いた数学的に証明されたVM隔離をクラウドで初めて実現
  • エージェントAIの普及でCPU需要が急増する中、192コアの高密度設計が新たな計算基盤として台頭
  • 移行にはArm対応状況の確認から段階的に進めるのが安全。AWS TransformやSavings Dashboardが支援ツールとして利用可能
WooCommerceモノレポのビルドが大幅高速化、コールドビルド60%減。メモリ使用量84%削減

WooCommerceモノレポのビルドが大幅高速化、コールドビルド60%減。メモリ使用量84%削減

WooCommerceのモノレポ(単一リポジトリ)を使った開発において、ビルドにかかる時間とメモリ消費が大幅に改善された。2026年6月5日、Developer WooCommerce Blogで公開された記事によると、コールドビルド時間が60%削減、ウォッチ(ファイル監視)の準備完了時間が75%短縮、開発時ウォッチプロセスのメモリ使用量が84%削減されたという。

計測環境はM4 Maxプロセッサ(48GB RAM、macOS 26)で、ベースラインから顕著な低下を確認している。一連のプルリクエストによってビルドプロセスを再設計し、開発者体験を飛躍的に向上させた内容を解説しよう。

本記事では、実際に実施されたビルド最適化の技術的詳細と、今後のCIスループット向上計画を紹介する。

WooCommerceモノレポのビルドが抱えていた課題

WooCommerceモノレポのビルドが抱えていた課題

WooCommerceのコードベースは多数のパッケージと連携しており、開発時のwatch:buildコマンドは最大128個のプロセスを起動していた。それぞれがESM(ECMAScript Modules)やCJS(CommonJS)、webpackによる監視を分担し、さらにwireitモニターとPNPMのランチャープロセスが重なり、24.4GBものメモリを消費する状態だった。

このままでは開発マシンのリソースを圧迫し、CI(継続的インテグレーション)実行時にもジョブの待機時間が増大する。ビルド速度の遅延はフィードバックサイクルを長びかせ、生産性に悪影響を及ぼしていた。その根本原因を取り除くため、重複作業の排除とツールチェーンの刷新に着手した。

従来のビルド(Before)
コールドビルド時間 96秒
ウォッチ準備完了時間 132秒
ウォッチメモリ使用量 24.4 GB
改善後のビルド(After)
コールドビルド時間 38秒(60%減)
ウォッチ準備完了時間 33秒(75%減)
ウォッチメモリ使用量 3.9 GB(84%減)
※M4 Max / 48GB RAM / macOS 26 上での計測値。改善率はベースライン比較。

上記の数値が示す通り、ビルド時間とメモリ消費の両面で大幅な改善が実現された。この結果をもたらした主要な施策は以下の3段階に分けられる。

重複ビルドの排除とTypeScriptコンパイラからの脱却

重複ビルドの排除とTypeScriptコンパイラからの脱却

最初に取り組まれたのは、不必要なモジュール形式のビルド重複の解消だ。WooCommerceはESMとCJSの両方を配布しているが、内部のバンドラ(webpack)ではESMのみが消費されていた。にもかかわらず、開発フローでは常に両方を生成していたため、無駄なビルドコストがかかっていた。

PR #64876では、パブリッシング専用のプリパックビルドコマンドを新設し、普段の開発時にはESMのみを生成するよう分離した。これによりビルドプロセスがスリム化され、コールドビルドの短縮に寄与している。

続いて、TypeScriptのコンパイル基盤をtscからesbuildへ移行するための準備が行われた。型チェックは独立したLintステップに分離し、型定義ファイルの生成はパブリッシュ時のみ実行する方式に切り替えた。こうしてビルド本体からTypeScriptコンパイラを外し、高速なバンドラに置き換える土台が整った。

esbuildへの移行とビルド設定の一元化

esbuildへの移行とビルド設定の一元化

型チェックとビルドの分離が完了したあと、全パッケージのビルドをesbuildへ切り替えた。esbuildはGo言語で実装されており、TypeScriptのトランスパイルにおいてtscやBabelよりもはるかに高速だ。この移行だけでウォームビルドの速度は顕著に向上した。

しかし、急いで移行した結果、各パッケージに似たようなbuild.mjsファイルが散在するという新たな課題が生まれた。これに対処するため、PR #65422ではビルド用の内部パッケージ@woocommerce/internal-buildを新設し、従来の設定パッケージ(internal-ts-configinternal-style-build)を統合した。開発マシン上のスクリプトが整理され、今後の保守性も高まった。

Admin/Blocksによるパッケージビルド統合、メモリ大幅削減の鍵

Admin/Blocksによるパッケージビルド統合、メモリ大幅削減の鍵

一連の改善の集大成となったのが、PR #65254で実施されたAdminおよびBlocks向けのビルド統合だ。それまでwatch:buildコマンドが128プロセスも必要だった最大の要因は、Admin用webpackとBlocks用webpackがトランスパイル済みESMを外部パッケージとして消費していたことにある。

このPRでは、各パッケージのソースを直接AdminおよびBlocksのwebpackビルドに含める方式へと変更した。その結果、128プロセスが大幅に削減され、メモリ使用量が24.4GBから3.9GBへと激減した。ウォッチ準備時間も132秒から33秒へと4分の1に短縮されている。

トレードオフとして、パッケージのトランスパイルがesbuildではなくBabelで行われるため、コールドビルドの速度が一部でわずかに後退した(38秒)。しかし、webpackのファイルシステムキャッシュによって日常的な開発では体感されず、E2E(End-to-End)テストのCIジョブが約1分長くなる程度にとどまった。全体のメモリ削減と開発体験の向上に比べれば、十分に許容できる交換だったと言える。

次のターゲットはCIスループットの改善

次のターゲットはCIスループットの改善

ビルドプロセス自体の最適化が完了した現在、開発チームはCIのスループット向上に注力する方針だ。WooCommerceのCIはジョブをマトリクスシャーディングで分散実行しているが、GitHub Actionsのワーカーが枯渇しやすく、各ジョブの実行時間が短くなってもワーカー獲得に20分以上待たされる局面がある。

この問題を解消するため、同一ワーカー上で複数のタスクを並列実行する方式への移行が計画されている。ワーカー台数に依存しない設計に切り替えることで、CI全体のスループットを大幅に引き上げる狙いだ。さらに、E2Eテストスイートの高速化として、マルチサイト構成などを活用し、単一環境内でテストスイートを並列実行する手法も検討されている。

これらの施策が実装されれば、コードをプッシュしてからCIが完了するまでの時間がさらに短縮され、開発のスピードは一段と加速するだろう。

この記事のポイント

  • WooCommerceモノレポの開発ビルドが全面的に見直され、コールドビルド60%減、メモリ使用量84%減を達成
  • 重複ビルドの排除とesbuild移行により、トランスパイル速度が大幅に向上
  • Admin/Blocksへのパッケージ統合で128プロセスを一掃し、メモリ消費を劇的に低減
  • 今後のCIスループット改善では、ワーカー枯渇問題の解決と並列化が焦点
2026年5月ウェブ標準 CSS疑似クラスや遅延読み込み新機能まとめ

2026年5月ウェブ標準 CSS疑似クラスや遅延読み込み新機能まとめ

2026年5月、Chrome 148、Firefox 151、Safari 26.5が安定版としてリリースされた。CSSの疑似クラスやコンテナクエリ、メディア要素の遅延読み込みといった使い勝手を大きく左右する機能群がBaselineへと加わっている。

具体的な変化点は4つだ。開閉状態にスタイルを当てる :open 疑似クラス、名前だけで親を参照できるコンテナクエリ、カスタムプロパティを条件とするスタイルクエリ、そして <video> および <audio> のネイティブ遅延読み込み。これらはすべて、主要ブラウザの最新版で動作するBaseline Newly availableとなった。

この記事では、それぞれの機能が何を解決し、実際のコードでどう使うのか、概観を交えながら見ていく。リリースノートを追いきれていないフロントエンドエンジニアやWeb制作担当者は、この機会にキャッチアップしてほしい。

:open 疑似クラスが Baseline 入り

:open 疑似クラスが Baseline 入り

長らくFirefoxとChromeが対応していた :open 疑似クラスが、Safari 26.5のサポートによりBaseline Newly availableとなった。開閉状態を持つHTML要素を、開いているときだけまとめてスタイリングできる疑似クラスだ。

具体的には <details><dialog><select> といった要素に加え、カラーピッカーや日付ピッカーなどの <input> も対象になる。これまでは details[open] のように属性セレクタで個別に書く必要があったが、より読みやすく一貫性のあるコードにまとめられる。

:open 疑似クラスが解決するもの

従来の手法では、開閉のインタラクションを表現するために要素ごとに異なるセレクタを書く必要があった。たとえば <details> には details[open]<dialog> には dialog[open] を使う。しかし :open 疑似クラスなら、これらをひとつのセレクタで扱える。

この変化は、コードのメンテナンス性を大きく改善する。とくにデザインシステムを構築するチームでは、コンポーネントの状態管理がシンプルになる利点が大きい。

従来の書き方(Before)
details[open] {
  background: #f0f0f0;
}

dialog[open] {
  background: #f0f0f0;
}
※要素ごとにセレクタを個別に書く必要があった
改善後の書き方(After)
:open {
  background: #f0f0f0;
}
※ひとつのセレクタで開状態を横断的に扱える

このデモで示したように、:open 疑似クラスを使うと開状態の記述が一箇所に集約される。複数箇所に散らばっていたスタイルを一元管理でき、意図しないスタイル崩れも防ぎやすくなる。

活用シーンと注意点

実務では、FAQのアコーディオンやモーダルダイアログのスタイル定義で即座に役立つ。フォーム部品のピッカーUIにも適用されるため、一貫したブランド表現が可能だ。

ただし、すべての開閉要素が対象になるわけではない。<summary> をクリックして開く <details> のように、ブラウザが開閉状態をネイティブに管理する要素に限定される。JavaScriptで独自に開閉を管理するUI部品には反応しない点に注意が必要だ。

名前のみのコンテナクエリ

名前のみのコンテナクエリ

Chrome 148のリリースにより、名前のみのコンテナクエリ(name-only container queries)がBaseline Newly availableになった。コンテナクエリを書く際に、サイズやスタイルの条件を省略してコンテナの「存在」だけを条件にできる。

これまでは container-type プロパティでコンテナの種別を宣言し、かつ @container ルール内でサイズ条件を指定する必要があった。今回の変更で、単に名前でコンテナを参照するだけのクエリが書けるようになり、コードの冗長さが大きく減る。

名前だけでコンテナを参照する仕組み

具体的なコードを見てほしい。従来は「サイドバーという名前のコンテナが、ある幅を超えたら」というクエリが中心だった。新しい構文では「サイドバーという名前のコンテナがあるなら」という条件だけでスタイルを切り替えられる。

/* コンテナの名前を付与 */
#container {
  container-name: --sidebar;
}

/* サイズ条件なしで名前だけで参照 */
@container --sidebar {
  .content {
    padding: 2rem;
  }
}
従来のコンテナクエリ(Before)
container-type: inline-size;
container-name: –sidebar;

@container –sidebar (min-width: 300px) { … }
※container-type とサイズ条件が必須だった
名前のみのコンテナクエリ(After)
container-name: –sidebar;

@container –sidebar { … }
※サイズ条件なしでコンテナの存在を参照できる

この構文によって、container-type の宣言が不要になるケースが増える。名前の指定だけでコンテナを参照したい場面では、CSSの記述量が減り、可読性も上がる。

実務での活用ポイント

大規模なサイトでは、レイアウトのコンポーネント化が進んでいる。コンテナクエリは「親コンポーネントの状態で子のスタイルを決める」設計と相性が良く、名前のみの参照はこの流れを加速する。

たとえば「サイドバーがDOM上に存在するならカードのパディングを変える」といった、レイアウトの有無に応じた条件分岐が簡潔に書ける。メディアクエリでは制御しきれなかったコンポーネント単位のレスポンシブが、より直感的に扱えるようになる。

コンテナスタイルクエリとカスタムプロパティ

コンテナスタイルクエリとカスタムプロパティ

Firefox 151で style() クエリのサポートが加わり、カスタムプロパティを条件とするコンテナスタイルクエリがBaseline Newly availableとなった。これにより、サイズ以外の親コンテナのCSSプロパティを条件にスタイルを切り替えられる。

とりわけ大きな意味を持つのは、カスタムプロパティ(CSS変数)を条件に含められる点だ。たとえば --theme という変数が dark のときに子要素の背景色を変える、といったテーマ切り替えをCSSだけで完結できる。

カスタムプロパティを条件にしたクエリの実例

以下は、親コンテナで定義された --theme: dark というカスタムプロパティを検知し、子の .card にダークモード用のスタイルを適用するコードだ。

@container style(--theme: dark) {
  .card {
    background-color: #1a1a1a;
    color: #fff;
  }
}
親コンテナ: –theme: light
カードタイトル
ライトテーマのカードコンテンツ
親コンテナ: –theme: dark(検知)
カードタイトル
ダークテーマのカードコンテンツ
–theme: light(検知対象外)  –theme: dark(styleクエリが発火)

この仕組みを使えば、JavaScriptに依存せずにテーマの切り替えが可能だ。CSS変数を用いた設計基盤が整っているプロジェクトであれば、追加のスクリプトなしでダークモード対応の精度を上げられる。

サイズクエリとの使い分け

サイズクエリは「コンテナの幅が300pxを超えたら」といったレイアウト制御に強い。一方でスタイルクエリは「テーマ」「状態」「モード」といった意味的な条件に向いている。

両者は競合するものではなく、補完関係にある。たとえば「サイドバーが存在し、かつダークテーマのとき」にスタイルを変える、といった複合的な条件設計も可能になる。コンテナクエリ全体がBaselineへと進んだことで、CSSの表現力は確実に一段階上がったと言える。

video と audio のネイティブ遅延読み込み

video と audio のネイティブ遅延読み込み

Chrome 148で <video><audio> 要素に loading="lazy" 属性が導入された。これにより、画像やiframeと同じく、メディア要素をビューポート付近まで読み込まない動作をブラウザネイティブで制御できる。

サイトのファーストビューに動画を置くケースは増えているが、初期ロードでユーザーの帯域を圧迫する問題は長年の課題だった。この機能はJavaScriptのIntersection Observerを使った手動実装を、宣言的な属性ひとつで置き換える。

実装と効果

実装はきわめてシンプルで、<video> タグに loading="lazy" を追加するだけだ。特別なポリフィルやライブラリは不要で、対応ブラウザであればそのまま動作する。

<video src="hero.mp4" loading="lazy" controls></video>
loading=”lazy” なし(Before)
ページ読み込み直後 すべての動画リソースを先読み
※帯域を圧迫し、LCPが悪化する
loading=”lazy” あり(After)
ビューポート接近時 必要なタイミングで読み込み開始
※初期ロードの負荷が下がり、帯域を節約できる

効果は数値にも表れる。Squarespaceのエンジニアリングチームが公開した記事によれば、ネイティブ遅延読み込みによって動画の初期リクエスト数が大幅に減り、LCP(Largest Contentful Paint)の改善に貢献したという。詳細は同チームの技術記事「How To Use Standard HTML Video and Audio Lazy-Loading on the Web Today」を参照してほしい。

対応範囲と今後の展望

Chrome 148でサポートが始まったこの機能は、今後のブラウザ展開によってBaseline化が期待される。FirefoxやSafariの動向はまだこれからだが、loading="lazy" の属性自体は画像やiframeですでに確立された仕組みであり、メディア要素への拡張も自然な流れと言える。

未対応ブラウザでは属性が無視されるだけで壊れないため、今すぐ実装してもリスクは少ない。動画を多用するポートフォリオサイトやLPでは、とくに導入効果が大きい。

Document Picture-in-Picture API とその他のアップデート

Document Picture-in-Picture API とその他のアップデート

CSS以外でも重要な進展があった。Firefox 151でDocument Picture-in-Picture APIがデスクトップ向けに導入され、Web Serial APIもFirefoxデスクトップとChrome Androidでサポートが拡大されている。

Document Picture-in-Picture API の概要

従来のPicture-in-Picture APIは <video> 要素を常に前面の小窓で表示する機能だった。一方、Document Picture-in-Picture APIは任意のHTMLコンテンツを含むウィンドウを常に最前面に表示できる。

これにより、ビデオ会議の参加者グリッドや株価ティッカー、タイマーといったインタラクティブなオーバーレイを、ページ遷移後も維持できるようになる。デスクトップ向けのプログレッシブウェブアプリ(PWA)でとくに威力を発揮するAPIだ。

Web Serial API のプラットフォーム拡大

Web Serial APIは、マイクロコントローラーや3Dプリンター、開発ボードといったシリアルデバイスとウェブサイトが直接通信するための仕組みだ。Firefoxでは専用のサイト権限アドオンを導入することで安全に管理できる設計になっている。

Chrome 148ではAndroid向けにもサポートが拡大され、モバイルデバイスからシリアル機器を制御するユースケースが現実的になった。IoT分野や教育用途での活用が今後広がると見られている。

この記事のポイント

  • 2026年5月のブラウザ安定版で、複数のCSS機能とHTML属性がBaseline Newly availableに到達した
  • :open 疑似クラスで開閉状態のスタイリングが一元的に書けるようになった
  • 名前のみのコンテナクエリにより、サイズ条件なしで親コンテナの存在を参照できる
  • style() クエリでカスタムプロパティを条件としたテーマ切り替えがCSSだけで実装可能
  • <video><audio>loading="lazy" でメディアの遅延読み込みがネイティブ化され、初期ロードの負荷が軽減される
WooCommerce 10.8リリース!レビューメール自動化と各種高速化の全容

WooCommerce 10.8リリース!レビューメール自動化と各種高速化の全容

WooCommerce 10.8が2026年5月26日にリリースされた。今回のアップデートでは購入後のカスタマーレビュー依頼メールの自動化、カスタム配送業者の設定機能、クーポンコードの動的生成、そして管理画面のパフォーマンス改善が盛り込まれている。

動作条件としてWordPress 6.9以上が必要だ。WooCommerceを更新する前にWordPress本体を最新にしておく必要がある。管理画面の一貫性を保つWordPress 7.0への事前適合も含まれており、今後のスムーズな移行に向けた布石となるリリースだ。

WooCommerce 10.8の主な変更点

WooCommerce 10.8の主な変更点

WordPress 7.0向けの管理画面スタイル調整

WooCommerce 10.8には約15件のプルリクエストが含まれ、WordPress 7.0の新しい管理画面デザインとの整合性を確保した。対象となったのはフォームコントロールのサイズ、Select2ドロップダウン、ボタンの角丸、通知の色、メタボックス周りのスタイルだ。

従来、WooCommerceの一部画面では青系の管理画面用色が直接ハードコーディングされていた。これがテーマカラー変数に置き換えられ、ユーザーが設定した配色スキームに沿って境界線やホバー状態が変化するようになった。WordPressとWooCommerceを同時に更新すれば、管理画面全体の見た目に統一感が出る。

従来の管理画面(Before)
ボタン ハードコーディングされた青色で固定表示
通知バー WordPress標準テーマ色に非対応
WooCommerce 10.8の管理画面(After)
ボタン テーマカラー変数を参照し自動で配色が変わる
通知バー 選択した管理画面テーマに追従

管理画面の色が選んだテーマに合わせて変化するため、複数サイトを運営している場合でもサイトごとに配色を変えられ、管理ミスの防止にもつながる。

オフライン対応の管理画面

WooCommerceの管理画面がオフラインを検知するようになった。ブラウザのネットワーク接続が切れるとバナーが表示され、保存リクエストがネットワーク喪失で失敗した場合には明確な通知が表示される。

これまで接続の不安定な環境では保存失敗に気づかず、注文データや設定の消失につながるケースもあった。モバイル回線やカフェのWi-Fiなど、接続状態が変わりやすい場所で作業するストア運営者にとっては実用的な改善だ。

従来の動作(Before)
保存ボタン押下 何も起こらない(失敗に気づかない)
10.8の動作(After)
オフラインバナー 「ネットワーク接続がありません」と画面に表示
保存失敗時 「保存に失敗しました」と通知が表示される

パフォーマンス改善の詳細

パフォーマンス改善の詳細

SQLクエリの削減と高速化

WooCommerce 10.7から続くクエリ削減の取り組みがさらに進んだ。取引IDルックアップ用の索引が wc_orders テーブルに追加され、販売ピーク時の在庫予約に使われる wc_reserved_stock テーブルの索引も改善された。

加えてキャッシュプライミングが商品アーカイブ、商品編集画面、クラシックカート、グループ化商品、Store APIの商品スキーマに拡張された。これにより各パスでデータを1行ずつ取得する代わりにバッチロードできるようになり、データベースへの負荷が大きく下がる。

クーポンの _used_by メタデータは遅延読み込み化された。何千回も使われたクーポンをロードする際に全使用履歴をメモリに展開しなくなり、クーポン読み込み時のパフォーマンスが飛躍的に改善する。レイヤードナビゲーションのフィルターキャッシュにはデフォルトで上限が設定され、wp_options テーブルが無制限に肥大化するのを防ぐ。

これまでのクーポン読み込み(Before)
_used_by メタ 全使用履歴を一度にメモリ展開(数千件で著しい遅延)
10.8の遅延読み込み(After)
_used_by メタ 必要なタイミングまで読み込みを遅延(メモリ節約)

ベータ版からの修正点

10.8のベータテスト期間中に見つかった問題も解消された。 WC_Order::payment_complete() に追加予定だったチェックアウト証跡のバリデーション機能は最終版から差し戻され、このリリースには含まれない。

また wc_orders_meta テーブルの meta_key_value 索引から meta_value 列が誤って削除されたパフォーマンス回帰も修正された。注文メタデータの検索速度が低下する問題だったが、10.8で索引構成が復元されている。

新機能の詳細

新機能の詳細

カスタマーレビュー依頼メールの自動化

10.8の目玉機能の一つが、購入者に商品レビューを依頼する自動メール機能だ。WooCommerceの設定の「メール」タブから有効化でき、Action Schedulerを使って注文完了から設定した日数後に送信される仕組みだ。

注文がキャンセル、返金、削除された場合にはメールは自動キャンセルされる。全額返金された商品はレビュー対象から外されるため、購入者と商品の関係が切れた状態でのレビュー投稿を防げる。顧客はトークン付きの専用読み取り専用ページに誘導され、アクセシブルな5つ星評価のコントロールからレビューを投稿する。投稿されたレビューは「確認済み購入者」の商品レビューとして扱われる。

従来のレビュー収集(Before)
ストア運営者 手動でレビュー依頼メールを作成・送信
課題 タイミングが属人的で管理が煩雑になる
10.8の自動レビュー依頼(After)
WooCommerce 注文完了から指定日数後に自動でメール送信
顧客 専用ページから5つ星評価+テキストレビューを投稿

この仕組みで集まったレビューは確認済み購入者の証跡が残るため、レビュー全体の信頼性を高められる。商品ページの社会的証明を強化したいストアには有効な手段だ。

クーポンコードの自動生成機能

メールブロック内で使えるクーポンコード機能が自動生成に対応した。ストア運営者は割引額やクーポンタイプ、有効期限といったルールを設定し、メール送信時に受信者ごとのユニークなコードを動的に発行できる。

パーソナライズされたクーポンキャンペーンの運用が大幅に簡略化される。全員に同じコードを配布して拡散リスクを抱える必要がなくなり、1人1コードの安全な配布が可能だ。

メールテンプレートの同期とリセット

ブロックメールの投稿にバージョン、ソースハッシュ、同期日時といったメタデータが付与されるようになった。テンプレートが元の配布状態からどれだけ変更されたかを自動検知できる。さらに管理画面からワンクリックでメール本文をプラグイン配布時のオリジナル状態に戻せるリセット機能も追加された。

カスタマイズを重ねたメールテンプレートの管理は煩雑になりがちだが、変更箇所の可視化と即時リセットで運用負荷が下がる。

カスタム配送業者の設定

独自の配送業者を定義できるUIが追加された。業者名と追跡URLテンプレートを登録すれば、注文画面で業者ごとのフィルタリングや、カスタマイズされた追跡リンクを使って出荷状況を確認できる。

国内の小規模な配送業者や地域限定の物流サービスを使っているストアでも、統一された画面から追跡情報を管理しやすくなる。

APIの更新

APIの更新

REST APIとGraphQL

注文APIでは shop_order でないレコードの変換が拒否されるようになり、チェックアウトドラフト注文はデフォルトクエリから除外されるようになった。より明示的なデータ操作が求められる変更だが、意図しないデータ混入を防ぐ点でAPIの堅牢性が増した。

注目すべきはGraphQL APIの導入だ。デュアルコードとGraphQL APIがWooCommerceに組み込まれ、管理画面の「詳細設定」タブにGraphQL設定セクションが追加された。GETエンドポイントのトグル操作で有効にできる。ヘッドレス構成やモダンなフロントエンドスタックからWooCommerceのデータを柔軟に取得したい開発者にとって重要な布石となる。

そのほか商品公開時に発火する product.published ウェブフックトピックの追加や、商品管理権限のないユーザーに対する機密フィールド(ダウンロード、売上原価、仕入メモ)の除外など、セキュリティ面の強化も図られている。

REST API(従来)
データ形式 エンドポイントごとに固定のレスポンス構造
課題 過剰取得や過少取得が発生しやすい
GraphQL API(10.8で導入)
データ形式 クライアントが必要なフィールドだけを指定
利点 通信量の削減とフロントエンド開発の効率化

データベースの更新と注意点

データベースの更新と注意点

このリリースにはデータベース更新が含まれている。自動実行されるスケジュール更新の中では、ブロックメール投稿への同期メタデータ付与、WooCommerce 10.5で名称変更された分析データのインポート設定復元、meta_key_value 索引の調整、レビュー依頼用の専用ランディングページ作成などが行われる。

10.8の更新前には必ずサイト全体のバックアップを取得し、ステージング環境での事前テストを推奨する。またWordPress 6.9以上が必須条件となるため、WordPress本体のバージョンも事前に確認しておく必要がある。

この記事のポイント

  • WooCommerce 10.8は購入後のレビュー依頼メールを自動化し、確認済み購入者のレビュー収集を効率化する
  • 管理画面のオフライン検知機能が追加され、ネットワーク不安定環境でのデータ消失リスクが低減した
  • クーポンコードの自動生成やメールテンプレートのリセット機能で運用負荷を下げられる
  • SQLクエリの削減とキャッシュプライミングの拡大により、ストアフロントの応答速度が向上する
  • GraphQL APIの導入はヘッドレス構成やモダンフロントエンド開発への対応を見据えた布石となる