Transient API をカウンターとして使う


Transient API を使う理由

ここ1週間でよくアクセスされたリストのウィジェットを作るため、簡易アクセスカウンターが必要になった。カウンターの格納先としては投稿のカスタムフィールド (postmeta) やファイルもあり得るが、Transient API は有効期間の情報を持たせられる。

シンプルなカウンター機能

Transient のキー は特定の文字列+投稿 ID、値はアクセス回数とする。すると次のようなコードでOKのはず。2行目は三項演算子。intval() は (Transient キーを他でセットしていなければ)不要だが、想定外のミスに備えて。

// Transient キーを設定
$transient = 'LFTEST'.get_the_ID();

// カウンターを取得
$count = intval( get_transient( $transient ) ) ?: 0;

// カウンターをインクリメントして Transient を更新
set_transient( $transient, ++$count );

一定のタイミングでクリアする

必要なのは「最近よく読まれている」リストなので、このカウンタは一定期間ごとにクリアしたい。set_transient() は第三引数に「現在から有効期限が切れるまでの秒数」を持たせられるが、単純に WEEK_IN_SECONDS と指定するとセットするたびに期限が延びていくだけなので、一週間に一度でもアクセスがあると値が累積されることになる。

そこで、土曜日(のゼロ時)にカウンターをリセットすることにした。第三引数には期限(時刻)でなく期間(秒数)しか指定できないので、次のようにしてみた。

// Transient キーを設定
$transient = 'LFTEST'.get_the_ID();

// カウンターを取得
$count = intval( get_transient( $transient ) ) ?: 0;

// 土曜ゼロ時にクリアされるようにしてカウンターをインクリメント
set_transient( $transient, ++$count, strtotime('next saturday')-strtotime('now') );

オブジェクトキャッシュを回避する

その後 APCu をオブジェクトキャッシュとして使用したところ、不具合が生じた。

Transient API はオブジェクトキャッシュが利用可能であればそれを利用し、Options テーブルにはアクセスしない。それが Options API にはない Transient API ならではの仕様で、高速化に貢献している。

ただ、私の理解では、オブジェクトキャッシュは用意されたメモリを使い切ると設定した期限内でもクリアされてしまうことがあり、今回のように最長一週間値を保持させる仕組みとしては不向きである。

そこで、この Transient だけオブジェクトキャッシュを迂回して Options テーブルに書き込ませる方法を考えた。

get_transient() は冒頭で wp_using_ext_object_cache() によってオブジェクトキャッシュの有無を判定している。この関数はグローバル変数にタッチすることなく一時的にキャッシュの状態を変えられるようなので、 次のようなコードにすればよいはず。

wp_using_ext_object_cache() は現在の状態を返すので、最終行のようにしておけば、たとえ将来的にオブジェクトキャッシュを使わなくなってもコードを変更する必要がない。

// オブジェクトキャッシュを OFF にする
$objcache_status = wp_using_ext_object_cache( false );


// Transient キーを設定
$transient = 'LFTEST'.get_the_ID();

// カウンターを取得
$count = intval( get_transient( $transient ) ) ?: 0;

// 土曜ゼロ時にクリアされるようにしてカウンターをインクリメント
set_transient( $transient, ++$count, strtotime('next saturday')-strtotime('now') );

// オブジェクトキャッシュを 戻す
wp_using_ext_object_cache( $objcache_status );

おまけ:それでもオブジェクトキャッシュの恩恵は享受できる

wp_using_ext_object_cache() はグローバル変数にタッチしないので、たとえ上のようにOFFにしても、Transient API が内部的に呼び出している Options API レベルではオブジェクトキャッシュが使われる。

set_transient() はキャッシュオブジェクトが有効なら Options テーブルにはアクセスしない。しかしwp_using_ext_object_cache() でキャッシュをオフにすると add_option() が呼び出され、値は Optionsテーブルに挿入した後 オブジェクトキャッシュにも登録される。

結果として、上のコードで オブジェクトキャッシュをオフにして get_transient() を呼ぶと、(オブジェクトキャッシュにデータが残っていれば)Options テーブルにアクセスすることなく値が取得できる。

Posted by

on

in category