12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CakePHP3Advent Calendar 2016

Day 10

CakePHP3で簡単にFileCacheからfindできるようにするプラグイン

Last updated at Posted at 2016-12-10

m-shimaoと申します。
この記事はCakePHP3 Advent Calendar 2016の10日目の記事となります。
Fusic Advent Calendar 2016の記事でもあります。)
本日は稚拙ながら私がついさっき作ったFileCacheを使ったプラグインの紹介をさせて頂きたいと思います。

プラグイン

m-shimao/find-in-file-cache

できること

  • 特定のテーブルでaddBehavior()すれば、そのテーブルへのfind()がFileCacheを利用したものになる
    • まだテストが書けてないので全パターンを確認できてないけど、複雑なクエリじゃなければ大丈夫なはず…
  • そのため、そのテーブルに対してselectクエリをたくさん投げてもパフォーマンス的にはあまり気にしなくてよくなる。
  • 作ったばかりでまだサービスでの稼働実績がないので、使われる方はお気をつけください:bow:

インストール方法

m-shimao/find-in-file-cacheのREADMEをご参照ください

使い方

いま使いたいテーブルがcategoriesテーブルだとすると、
Model/Table/CategoriesTable.phpにてaddBehavior()を追加すれば使えます。

<?php
class CategoriesTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('FindInFileCache.FindInFileCache');
    }
}

find()を投げると、beforeFind()の中で既にFileCacheにデータがあるかをチェックし、
該当するデータが有ればそのデータを返して以降のイベントを行わないような動きをします。
FileCacheに無ければ、通常通りfindを実行後FileCacheにデータをキャッシュします。
キャッシュの有効期間はデフォルトで15分としています。
その他キャッシュの設定を変更したい場合はapp.phpでfind-in-fileというキーでキャッシュの設定を追加して頂ければ
そちらの設定を見に行くようになります。設定例↓です。

<?php
return [
...
    'Cache' => [
        ...
        'find-in-file' => [
            'className' => 'File',↲
                        'className' => 'File',↲
                        'prefix' => 'myapp_cake_static_record_',↲
                        'path' => CACHE . 'models/',  //pathをmodelsと同じにする↲
                        'duration' => '+1 hours',↲    // cacheの有効期間を1時間にする
        ],
        ...
    ],
...
];

注意点

  • 対象のテーブルは基本的にデータが変わらないもの or データが変更されてもすぐに反映される必要が無いものを想定しています。
    例えばbookmarksなど頻繁に更新されるテーブルに対して設定しても使いものにならないと思うので、
    お使いの際はテーブルの性質をご考慮ください
  • join等にも対応しているはずですが、まだテストできていません…。余り複雑なクエリでは使わないほうがよいかもしれません。

開発でハマった点

もともとQueryオブジェクトに

$query->cache($cacheKey);

とするとcacheからデータのfetch・データのstoreをうまいことfindの前・後に挟んでくれるようになるメソッドがあるので
それを使えば簡単に実装できそうだと思っていたのですが、
cacheからのfetchのタイミングがbeforeFindの前だったので、うまく使えませんでした…。
結局上述のようにbeforeFindの中でキャッシュにアクセスして、あったら以降のイベントをStopするような実装にしました。

(2017/1/31追記) コメント頂いた件の改修

  • 間が空いてしまいましたが…、@chinpei215さんよりご指摘頂いた下記の2点に対して対応しました:bow:
    • mapReduce(), formatResults()がセットされているクエリの場合に実行の度にデコレードが実行されてしまい、結果セットが壊れてしまうバグ
    • 同じクエリオブジェクトで二回以上クエリが実行される場合に、2回目以降はcacheを無効化する対応

今後の改修予定

  • テストコードの追加。合わせてある程度複雑なクエリへの対応
  • cacheを使用しないオプションの追加。
  • データがsaveされたらキャッシュを削除する処理を追加。
  • いまはクエリの内容毎にキャッシュのキーを変えているのですが、クエリはfind()->all()だけに統一して、その結果セットをwhere句やorder句などの内容に応じてゴニョニョして返すようにしたいです。

雑記

  • 昨日はymm1x様のCakePHP3のbakeによるコード自動生成のプラクティスでした。
    ちょうど業務で「そろそろbakeいじるか〜」と思っていたタイミングだったので、大変参考になりました:bow:
  • 実は元々クエリログを簡単に出すプラグインを作ろうと思っていたのですが、4日目のYKEI_mrn
    様のCakePHP3 のログをためすを見てapp.phpの設定だけでできることを恥ずかしながら初めて知りました。
  • 何が言いたいかというと、Advent Calendar登録すると内容が被らないようにこれまでの記事を一通りみるので、すごくためになるなぁと思ったのでした:grin:
12
5
4

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
12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?