Amazon RDSをCakePHPで使う

RDSと素のMySQLの違いとして、よく言われるのが以下のような話。

  • timezoneがUTC固定
  • maintenance window等で止まるときがある

CakePHPでRDSを使う上で、どうやって対応したものかやってみた。

timezoneがUTC固定

接続するたびにSET time_zone = '+9:00'を実行すればいいわけだが、どこで実行するかが問題で、今回はapp_model.phpに書いてみました。
この対策だと、Modelが初期化されるたびにSET time_zoneしてしまうので、本当はConnectionManagerに手を入れるのが一番いいのだろうが、Cake本体に手を加えたくなかったので目をつぶることにした。

<?php
class AppModel extends Model {
	var $_timezoneInit = false;

	function setDataSource($dataSource = null) {
		$r = parent::setDataSource($dataSource);
		if ($this->_timezoneInit === false) {
			$db =& ConnectionManager::getDataSource($this->useDbConfig);
			$db->query("SET time_zone = '+9:00'");
			$this->_timezoneInit = true;
		}
		return $r;
	}

}

maintenance window等で止まるときがある

maintenance windowとbackup windowはなくすことはできないので、まずは最もサービスにインパクトがなさそうな時間帯がいつかを考える。一般的に日本のインターネットは3amから8amくらいが一番トラフィックが少ない(参考)ので、この時間帯に全部終わらせてしまうのが望ましい。
というわけで、backup windowは日本時間の7amから8amの間、maintenance windowは毎週月曜日の早朝3amから7amまでの間に入れることにした。これをUTCに直して、こんな感じで指定する。

rds-create-db-instance (略) --preferred-maintenance-window Sun:18:00-Sun:22:00 --preferred-backup-window 22:00-00:00


ちなみに、JSTからUTCの変換をするrubyワンライナー

ruby -e 'p Time.mktime(2010,4,5,3).utc'


次に、今回の案件は、データがリアルタイムで反映されなくてもいいし、取得時に最新データと比べてタイムラグがあってもいい(最新情報である必要がない)ので、controller側で「DBに接続できなかったらデータはlocalにファイルで書き出し、データは古いキャッシュを使う」ようにした。
controllerの初期化時、最初にDBアクセスが発生するのはAppController::constructClasses()なので、本体を呼ぶ前にDB接続をチェックするフックを各controllerに入れた。

<?php
App::import('Core', array('ConnectionManager'));

class HogeController extends AppController {

	var $name = 'Hoge';

	function constructClasses() {
		// データベースの接続確認をする
		$conn = ConnectionManager::getInstance();
		@$ds = $conn->getDataSource('default');
		if (!$ds->isConnected()) {
			// このへんで各種回避処理をする
			exit;
		}
		return parent::constructClasses();
	}