うんこ

昨年の目標を見たらちゃんと元日にかいてたんだなーと気づいた今日この頃。
全力は個人的には出したつもりだけど120%出せたかといえば、もっと出せたんだろうな〜と思う。
結果として仕事の目標は達成できなかったです。

さて、元旦じゃなくなってしまいましたが、目標を残すのはいいことだなーとつくづく思うので、今年も目標を書き残しておきます。


仕事の目標は「一発当てる」です。

もう、当てるまでは走り続けるしかないなあと、去年も一昨年も苦しかったですが、3度目の正直という言葉もあるので、性懲りもなくあてに行きます。

本当は1年半くらいを見越した野望があるのですが、さすがにこう外していると当てるまでは次に進めないので当ててから考えます。

いや、言いたい。
いや、いわない。。。


プライベートはというと、正月休みも土日も全返上してやった最初の仕事がこけてしまい失意の中で自分の自信
を取り戻すためのある目標を立てました。

成功するために肉体的自信を取り戻すためのの5ヵ年計画

2010年---やせる→成功体験から仕事も成功
2011年---オープンウォータースイミングレース出場&腹筋を割る→みなぎる自信→仕事で大成功
2012年---根性系レース(マラソンorトライアスロン)完走→仕事も充実→モテる
2013年---もはやオーラすら出ているが翌年の目標のスタートラインに立つために仕事を死ぬ気でがんばり成功
2014年---ガチアイアンマンレース参戦&完走→もはやレジェンド→不安は一切無いポジティブマンへ

昨年の計画は「やせる」です。
これは見事に達成することが出来ました。
今年は次の元旦までに腹を割ります。

仕事も、きっと今年への布石になっていると思います。


ちなみに、もう一つヤビツ峠を50分以内で上るというとてつもない目標も立てておきます。
55ならいけると思います。
あえて50以下を目指します!!


あと去年立ててた飲むという目標は今年はありませんが、出来るだけ多くの人に合おうと思います。

sfDoctrineMasterSlavePluginを使う時のTips

Symfonyアドベントカレンダー2010には忙しすぎて参加していません(ごめんなさいx2)
Doctrine on symfony1.4でマスタースレーブ | やぐにっきに、ちょっと補足しておくと喜ぶ人がいる気がしますので書いてみます。

その前に

symfonyの情報を沢山提供してくださっている皆様、いつも大変お世話になっております。
ありがとうございます。
来年もどうぞよろしくお願いいたします。

来年こそは心を入れ替えて、僕もお役に立てるといいなと思います。

sfDoctrineMasterSlavePluginについて

さて記事にあるように、symfony1.x + Doctrine1.xなサービスでデータベースをmaster-slave構成で冗長化する場合、sfDoctrineMasterSlavePluginを使うというのはとても良い選択だと思います。

sfDoctrineMasterSlavePluginの素晴らしいと思う点は主にこんな所でしょうか?

  • 複数のslave複数のmasterに対応
  • 導入が容易
  • Queryがmasterに向いているのかslaveに向いているのかlogを見れば一目で分かる

本当に素晴らしいですね。
でも、局所的にバッドノウハウ的なTIPSは必要です。

補足説明させていただきたいのは、明示的にMasterからSELECTする方法についてです。

明示的にMasterからSelectするシチュエーション

記事にもあるようにsfDoctrineMasterSlavePluginは黙っていれば、参照系はmaster、更新系はslaveに振り分けてくれます。
さらにtransactionの中では参照系でもmasterを使ってくれます。

しかし、サービスによってはtransactionの外でもmasterから参照したい時というのはあります。
例えば下記のような状況が考えられます。

1. table lockが多発するためtransactionをあえて使わない
僕の場合、そこそこアクセスのあるソーシャルアプリでは緩い実装をするため、ユーザーが喜ぶ処理から悲しむ処理に向かってTableに更新をかけ、最低限不整合がおこらないレベルでトランザクションをあえて使いません。

2. ユーザーのスコア表示などわずかな遅延も許されない情報を参照したい
MasterSlaveの遅延はありきで考えています。

明示的にMasterからSELECTする方法

前置きがとっても長くなりましたが、Masterを参照するために僕はこのような方法を使っています。

1. Doctrine_Tableを継承したmyDoctrineTableを作る
2. *Table の継承元をmyDoctrineTableに全て変更する。
3. Masterを参照したい時にcreateQuery()の代わりにcreateMasterQuery()を使う

myDoctrineTable

<?php

class myDoctrineTable extends Doctrine_Table
{
    public function insert(array $fields)
    {
        return $this->getMasterConnection()->insert($this, $fields);
    }

    public function update(array $fields, array $identifier)
    {
        return $this->getMasterConnection()->update($this, $fields, $identifier);
    }

    public function delete($identifier)
    {
        return $this->getMasterConnection()->delete($this, $identifier);
    }

    public function replace(array $fields, array $keys)
    {
        return $this->getMasterConnection()->replace($this, $fields, $keys);
    }

    public function getMasterConnection()
    {
      return ProjectConfiguration::getActive()->getMasterConnection($this->getConnection());
    }

    public function getSlaveConnection()
    {
      return ProjectConfiguration::getActive()->getSlaveConnection($this->getConnection());
    }

    public function createMasterQuery($alias = '')
    {
        if ( ! empty($alias)) {
            $alias = ' ' . trim($alias);
        }

        return Doctrine_Query::create($this->getMasterConnection(), 'Doctrine_Query')
            ->from($this->getComponentName() . $alias);
    }
}

その他のメソッドはおまけです。


Doctrineはとても便利ですが、メモリを食い過ぎることと、処理が複雑すぎるという欠点があります。
メモリを食い過ぎるとスワップが発生しやすくなりますし、処理が複雑すぎると泥臭い改造が出来なくなります。

ORMを選択する時は、初期開発コストと運用コストを天秤にかけてじっくりと選択することをおすすめします。

これからはDoctrine2かもしれませんが、相変らずメモリは食うみたいですので開発前には慎重に検討した方が良いかもしれません。

クロージャー

create_functionをつかっちまった。
クソやろーとか言ってごめんなさいごめんなさい。

こんな風に引数渡せるのね。

<?php
class myDoctrineCollection extends Doctrine_Collection
{
    public function sortBy($column, $order = 'asc')
    {
        $sortOrder = (strtolower($order) == 'desc') ? -1 : 1;
        usort($this->data,  function ($a, $b) use ($column, $sortOrder) {
            return strcmp($a->{$column}, $b->{$column}) * $sortOrder;
        });
        return $this;
    }
}

use++

ちなみにこれはsymfony1.4のDoctrineで、Doctrine_Collectionでソートをするための拡張

findしたファイルを一括で全角→半角変換する"PHP"のワンライナー

find data/*.yml | xargs php -r 'array_shift($argv); foreach ($argv as $file) {if(file_exists($file))file_put_contents($file, mb_convert_kana(file_get_contents($file), "rk", "utf-8"));}'

あえてのPHP! w

[screen] 起動時にsshとかtabとか開く

.screnrcに書いておくとデフォルトの起動時のタブを設定できる

screen -t zsh 0
screen -t server1 1 ssh hoge@example.com
screen -t server2 2 ssh hoge@2.example.com
screen -t server3 3 ssh hoge@3.example.com
select 1

再起動したり、一回きれいにリフレッシュしたりしたいときに便利

おっぱいとちくび

先輩の「ゲームはちくびに通じる」という名言にえらく感銘を受け、
ついうっかり女性も一緒の食事の席でちくびがいかにゲームに通じるか語りすぎてしまった。

帰り道、ふと、もうオレは三十路の直前で若くはなくなってきていることに気づき、インターンの子とか22とかの子にこんなに「ちくびちくび」言ってたら、セクハラで訴えられても反論のしようが無いということに気づいてしまった。

「あーやべ」


でも、今日ほど改めてちくびとおっぱいについて考えたことはなく、
「ちくびじゃなくて、おっぱいじゃダメなのか?」ときかれたら、「おっぱいじゃダメなんです!ちくびじゃなきゃダメなんです!」と声高に叫びたくなる気持ちはどこから湧き出てくるものなのか、必死に考えた。


で、答えがわかった。

”ちくびはおっぱいの必要十分条件である”が、”おっぱいはちくびの必要十分条件とはならない”のだ。

不思議である。必要十分条件であるならばその裏もまた真であるはずである。
数学で何度もそう習った。

だがしかし、「ちくびはおっぱいの必要十分条件である」という命題はこの公式を覆してしまうのだ。



わかりやすく説明しよう。

「ちくびのないおっぱい」と「おっぱいのないちくび」を想像して欲しい。


ちくびの無いおっぱいにはなにも興奮しないと思わないか?
が、しかし、たとえおっぱいがなかろうと、そこに凛と聳え立つ気丈なちくびさえがあれば僕達の欲望は満たされる。


そう、つまり僕たちの言う真のおっぱいはちくびなのだ。




ところで、なぜ、こんなにもちくびとおっぱいについて考えたかということを振り返ってみる。


「ゲームはちくびに通じる」とは


僕たちは生まれ出てちくびに出会った瞬間から、ちくびに夢中になりちくびが無いと落ち着かなくなり、ちくびに吸い付きたいとちくびを愛した。


そして、時がたち一度ちくびを離れると、「ちくび?はあ?えろーい」とちくびを敬遠しちくびと向き合うことをしなくなった。


しかし、さらに時がたち、いつの日か僕たちはまたちくびに興奮し、ちくびが大好きになっている。
そして、僕たちがちくびを愛するがゆえに、また新しい乳首を愛する命が生まれてくるのだ。


そう、これは僕たちとゲームとの付き合い方に似ている。

小学校に上がるかあがらないかの時、スーパーマリオにであい夢中になり、ドラクエをするために仮病を使おうとして母親に怒られ、学校でもゲームの話ばかり、みんながゲームに夢中になっていた。


しかし、やがて大きくなると、ゲームばかりやっているやつはオタクだ。引きこもりだとゲームと距離を置く。
ゲームばかりやってるやつとも距離を置く。


しかし、今ソーシャルゲームやDS、WiiPSPPS3Xboxネトゲといった人それぞれ関り方は違うもののみんな結局ゲームが大好きでみんなやっている。


そして、ゲームが好きな僕たちが作るゲームでまた、新たなゲーム好きが生まれてくれるといいなと思っているのである。

そう、そんな素敵な存在はちくびに他ならない。
おっぱいではないのだ。


もちろん僕はおっぱいが好きだ。
おっぱいより好きなものを、おっぱいに一切かすらないものからあげよという問題を出されたら、正直答える自信が無い。

というか、さっきから2時間以上考えているがそんなものはないといっていいくらいおっぱいが好きだ。

だが、しかしそんなおっぱいがすきなのも、ちくびがあってからこそであり、ちくびこそがおっぱいであり、ちくびあってのおっぱいなのだ。


ちくびを隠してもおっぱいだ。でもその先にちくびがあるからおっぱいなのだ。
ちくびを隠したおっぱいを恥ずかしがる人はなかなかいない。
しかしちくびを恥ずかしがる人は星の数ほどいる。


ちくびは特別なのだ。


だから、それに通じるゲームも特別ですばらしい。


ちくびとおっぱいとゲームに感謝

phpの配列を文字列にするあれこれ

DoctrineでResultCacheを使うときに、timeを扱うフィールドがあると効果がないので、キャッシュキー生成の部分をオーバーライドしている。

元のコードは

<?php
// 〜略〜
    public function calculateQueryCacheHash()
    {
        $dql = $this->getDql();
        $hash = md5($dql . var_export($this->_pendingJoinConditions, true) . 'DOCTRINE_QUERY_CACHE_SALT');
        return $hash;
    }
// 〜略〜

こんなふうになってて、md5とvar_exportって重くね?ってふと気になった。

そこで、実際の所どうなのか、僕の思いつく配列を文字列に変換するあれこれの方法でパフォーマンスを調べてみた。

<?php
require_once 'Benchmark/Timer.php';
$array = array(
  'a'    => '1234567',
  'hoge' => 345,
  'time' => '12/21 00:30',
);
$b = new Benchmark_Timer();
$b->start();
for ($i = 0; $i < 10000; $i++) {
  $hash = serialize($array);
}
echo "serialize: $hash (".strlen($hash) .")\n";
$b->setMarker('serialize');

for ($i = 0; $i < 10000; $i++) {
  $hash = json_encode($array);
}
echo "json_encode: $hash (".strlen($hash) .")\n";
$b->setMarker('json_encode');

for ($i = 0; $i < 10000; $i++) {
  $hash = implode(":", $array);
}
echo "implode: $hash (".strlen($hash) .")\n";
$b->setMarker('implode');

for ($i = 0; $i < 10000; $i++) {
  $hash = implode(":", array_keys($array)) . "=>" . implode(":", $array);
}
echo "implode + array_keys: $hash (".strlen($hash) .")\n";
$b->setMarker('implode + array_keys');

for ($i = 0; $i < 10000; $i++) {
  $hash = var_export($array, true);
}
echo "var_export: $hash (".strlen($hash) .")\n";
$b->setMarker('var_export');

$b->stop();

$b->display();

結果

serialize: a:3:{s:1:"a";s:7:"1234567";s:4:"hoge";i:345;s:4:"time";s:11:"12/21 00:30";} (75)
json_encode: {"a":"1234567","hoge":345,"time":"12\/21 00:30"} (48)
implode: 1234567:345:12/21 00:30 (23)
implode + array_keys: a:hoge:time=>1234567:345:12/21 00:30 (36)
var_export: array (
  'a' => '1234567',
  'hoge' => 345,
  'time' => '12/21 00:30',
) (73)
                                                                                                                                    • -
marker time index ex time perct
                                                                                                                                    • -
serialize 1285050970.27137800 0.027326 7.44%
                                                                                                                                    • -
json_encode 1285050970.30083500 0.029457 8.02%
                                                                                                                                    • -
implode 1285050970.32389300 0.023058 6.28%
                                                                                                                                    • -
implode + array_keys 1285050970.39212300 0.068230 18.58%
                                                                                                                                    • -
var_export 1285050970.44748400 0.055361 15.07%
                                                                                                                                    • -

大方の予想通り,Doctrineの方法(var_export)は遅い!

ここで、あ!そうかDoctrineはオブジェクトにも対応してるんだな!ってことに気づいてオブジェクトにも対応するように修正

<?php
require_once 'Benchmark/Timer.php';
$array = array(
  'a'    => '1234567',
  'hoge' => 345,
  'time' => '12/21 00:30',
  'obj'  => new Test(5, 'abcd', '123'),
);
class Test
{
  public $a;
  protected $b;
  private $c;

  public function __construct($a, $b, $c)
  {
    $this->a = $a;
    $this->b = $b;
    $this->c = $c;
  }
}
//test
$b = new Benchmark_Timer();
$b->start();
for ($i = 0; $i < 10000; $i++) {
  $hash = serialize($array);
}
echo "serialize: $hash (".strlen($hash) .")\n";
$b->setMarker('serialize');

for ($i = 0; $i < 10000; $i++) {
  $hash = json_encode($array);
}
echo "json_encode: $hash (".strlen($hash) .")\n";
$b->setMarker('json_encode');

for ($i = 0; $i < 10000; $i++) {
  $hash = var_export($array, true);
}
echo "var_export: $hash (".strlen($hash) .")\n";
$b->setMarker('var_export');

$b->stop();

$b->display();

結果

serialize: a:4:{s:1:"a";s:7:"1234567";s:4:"hoge";i:345;s:4:"time";s:11:"12/21 00:30";s:3:"obj";O:4:"Test":3:{s:1:"a";i:5;s:4:"*b";s:4:"abcd";s:7:"Testc";s:3:"123";}} (158)
json_encode: {"a":"1234567","hoge":345,"time":"12\/21 00:30","obj":{"a":5}} (62)
var_export: array (
  'a' => '1234567',
  'hoge' => 345,
  'time' => '12/21 00:30',
  'obj' => 
  Test::__set_state(array(
     'a' => 5,
     'b' => 'abcd',
     'c' => '123',
  )),
) (172)
                                                                                                                            • -
marker time index ex time perct
                                                                                                                            • -
serialize 1285052647.37092000 0.041734 10.71%
                                                                                                                            • -
json_encode 1285052647.40332300 0.032403 8.31%
                                                                                                                            • -
var_export 1285052647.49001500 0.086692 22.25%
                                                                                                                            • -

なんと、、、全然遅い。

僕の結論

serializeのほうがjsonより少し早いけど、データの小ささ、多言語との互換性を考慮すると、jsonを使うのがよさそうな気がする。
ただし、Objectを含み、privateメンバの値も重要だよってときは、serializeが良い。
というか結局本気で取り組むなら、inplode, json, 無変換を臨機応変に使い分けるべしって感じ



おまけmd5を付けた場合

                                                                                                                          • -
marker time index ex time perct
                                                                                                                          • -
serialize 1285054112.44181000 0.041134 10.52%
                                                                                                                          • -
serialize+md5 1285054112.50591500 0.064105 16.40%
                                                                                                                          • -
json_encode 1285054112.53768200 0.031767 8.13%
                                                                                                                          • -
json_encode+md5 1285054112.59036000 0.052678 13.47%
                                                                                                                          • -
var_export 1285054112.68119600 0.090836 23.23%
                                                                                                                          • -
var_export + md5 1285054112.79160600 0.110410 28.24%
                                                                                                                          • -

var_exportを使っちゃうより全然早いので、復元しない限りは使ったほうがよいきがする



こんな感じで、結構アクセスが多くなりそうなサービスの開発にDoctrineを選択したことを今は正直後悔している。

Doctrineはイニシャルの開発工数は慣れれば減りそうだが、パフォーマンスを気にし始めるとチューニングで死ぬ。
だったら、いろはを知っているPropelを選べばよかった。。。