ほとんどこんな事例ないと思うけど、メモ。
CakePHPでバッチを作る際に、Model->query()でSQLを何回も発行するような組み方をしてしまうと、メモリが足りなくて処理落ちする事がある。まぁ、そもそもこんなバッチ組んでるのどうなの?って思うけど。
<?php
class HogeShell extends AppShell {
public $uses = array('Hoge');
public function main() {
for ($i=0; $i<100; $i++) {
// メモリ足りなくて死ぬ
$result = $this->Hoge->query('...');
}
}
}
前提
- CakePHP 2.4.x
Model->find() などはクエリキャッシュできる
Modelにはセッション単位でのクエリキャッシュする機能がある。Model-> cacheQueries に true を設定すると、クエリキャッシュが有効になる。デフォルトでは無効。あくまでセッション単位なので、活用するシーンはあまりなさそうだけど。
<?php
class Hoge extends AppModel {
public $cacheQueries = true;
}
Model->query()はデフォルトでクエリキャッシュする
まさにこれがハマリポイント。
query() は本質的に分離された機能のため、$Model->cacheQueries は無視されます。クエリ実行のキャッシュしないようにするには、2つ目の引数にfalseを指定してください。query($query, $cachequeries = false)
マニュアルにしっかり書かれているが、特別扱いのため、別個で指定してあげないとNG。それにしてもなぜModel->query()はデフォルトでクエリキャッシュが有効なのか。謎。
public function fetchAll($sql, $params = array(), $options = array()) {
if (is_string($options)) {
$options = array('modelName' => $options);
}
if (is_bool($params)) {
$options['cache'] = $params;
$params = array();
}
// ここで無指定ならデフォルトで有効にしている…
$options += array('cache' => true);
$cache = $options['cache'];
// snip
}
なので、クエリキャッシュする必要がなく、何回もquery()を実行する場合は、引数に false を渡してあげよう。
<?php
class HogeShell extends AppShell {
public $uses = array('Hoge');
public function main() {
for ($i=0; $i<100; $i++) {
// メモリ足りて無事終わる
$result = $this->Hoge->query('...', false);
}
}
}
そして、こういったバッチを書いている状況の場合、もっと改善できないか検討しよう。