こんにちは。小川です。
先日、symfony(v1.2.7)で本番(prod) 環境に設定した場合に開発(dev)環境の数倍のメモリが消費されるという状況に陥ってしまいました。原因を追及した結果、ルーティングの設定に問題があることが発覚したので、今日はそのことを書こうと思います。
原因先には述べてあるとおり、ルーティングの設定に問題がありました。symfony1.2ではルーティングのキャッシュということを行っており、そのキャッシュが肥大化してメモリを大量に消費する原因となっていました。
対策としてルーティングのキャッシュを無効にしてキャッシュファイルの読み書きを行わないように設定ファイルを修正したところ、上記の問題は無事に解決しました。
<strong>追記@2009/09/28</strong>
symfony1.2.9以降では初期状態でキャッシュが無効になるように設定されています。symfony1.2.9からのgenerate:appタスクでアプリケーションの生成を行った場合は修正は必要ありません。
というわけで今回は、
1. ルーティングのキャッシュの仕組み
2. なぜそんなにもキャッシュが肥大化してしまったのか
3. ルーティングのキャッシュを無効にする方法、その他対策
上記の3つをテーマにお話しさせていただきます。
symfony 1.2のルーティングについては以前書いた「symfony 1.2のルーティングまとめ」という記事をご覧ください。
symfonyでは通常、 /モジュール名/アクション名(/パラメータ) という形式のURLをとりますが、このURLとモジュールおよびアクションを結びつける仕組みをルーティングと呼んでいます。例えば、/article/newというURLはarticleモジュールのnewアクション、といったものです。
そしてそれぞれのルーティングには名前がついています。/モジュール名/アクション名(/パラメータ) という形式はdefaultという名前がつけられています。これ以外にもホームページにあたるhomepageや各モジュールのインデックスにあたるdefault_indexがデフォルトで定義されています。
実際に symfony generate:app コマンドでアプリケーションを作った際に、routing.ymlには以下のような定義がされています。
homepage:
url: /
param: { module: default, action: index }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/*
:(コロン)ではじまる項目はリクエストパラメータとして扱われます。先ほど例に挙げた/article/newというURLはdefaultにマッチし、$request->getParameter(‘module’)としてリクエストオブジェクトからパラメータを取得すればarticleという文字列が取れます。moduleとactionは特別なパラメータで、名前の通りモジュール名とアクション名を特定するための値です。
またhomepageやdefault_indexでparamという項目の中でも同じようにmoduleやactionが指定されていますが、これはURLがマッチした際にURLに含まれていなくてもリクエストパラメータとして指定するというものです。
*(アスタリスク)は任意のリクエストパラメータを/(スラッシュ)区切りで記述可能にするもので、 /article/show/year/2009/month/08/day/20 というURL でアクセスが来た場合にarticleモジュールのshowアクションが呼び出され、リクエストオブジェクトからはそれぞれyearが2009, monthが08, dayが20のように取得することができます。/article/show?year=2009&month=08&day=20 と同じようなものですが、スラッシュ区切りで来ている場合は$_GETには直接はいらず、symfonyが内部でリクエストに定義するということを行うという違いがあります。
もし /article/2009/08/20 というURLでarticleモジュールのshowアクションに行くようにし、2009, 08, 20という値をそれぞれyear, month, dayという名前のリクエストパラメータとして指定したい場合は以下のようにrouting.ymlに定義します。
article_show_at_date:
url: /article/:year/:month/:day
param: { action: show }
これでarticle_show_at_dateがルーティングに定義されます。このルーティングをアプリケーション内から呼び出すときにはlink_toやurl_forといったヘルパーや、アクションのredirectメソッドがよく知られていると思います。
例えばurl_forであれば url_for(‘@article_show_at_date?year=2009&month=08&day=20’) のように指定します。実際にurl_forやredirectがURLのパースを行う処理はsfWebControllerのgenUrlメソッドが呼び出されており、更にその中でsfPatternRoutingクラスのgenerateメソッドを呼び出しており、パース処理が行われています。
このパース処理ですが、routing.ymlを展開した後、各ルーティングを分解して正規表現に変換して1つ1つマッチングを行うなどの処理をして、最終的にURLに変換を行うという流れになります。
この @article_show_at_date?year=2009&month=08&day=20 を /article/2009/08/20 に変換する処理をキャッシュすることがルーティングのキャッシュになります。実装方法は単純で、前者の内部的なURLにコンテキストの情報を付与したものをキーにしてキャッシュを作成する実装になっています。
キャッシュのキー を具体的に生成してみます。
// ルーティングの名前
string 'article_show_at_date' (length=20)
// ルーティングのパラメータ
array
'module' => string 'default' (length=7)
'action' => string 'index' (length=5)
'sf_culture' => string 'en' (length=2)
'year' => string '2009' (length=4)
'month' => string '08' (length=2)
'day' =>