text型のカラムを明示的にINSETRTしなかったときのNULL値の挙動

後輩に訊かれてどうだったっけ?と思って調べたので、まとめておく。

まとめ

  • text型でINSERT時に明示的に情報を入れない場合、カラムがNOT NULLであったときは、""が入る。NOT NULLでない場合はNULLが入る。

作業ログ

# not null制約をつけている場合
mysql> create table fuga ( id int unsigned not null AUTO_INCREMENT primary key, text text not null ) Engine=InnoDB;
Query OK, 0 rows affected (0.13 sec)

mysql> insert into fuga () values ();
Query OK, 1 row affected, 1 warning (0.06 sec)

mysql> select * from fuga;
+----+------+
| id | text |
+----+------+
|  1 |      |
+----+------+
1 row in set (0.00 sec)

mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 5.1.58    |
+-----------+
1 row in set (0.02 sec)

# not null制約をつけなかった場合
mysql> create table foo ( id int unsigned not null AUTO_INCREMENT primary key, text text );                                                                                                               

Query OK, 0 rows affected (1.28 sec)

mysql> insert into foo () values ();                                                                                                                                                                      
Query OK, 1 row affected (0.02 sec)

mysql> select * from foo;
+----+------+
| id | text |
+----+------+
|  1 | NULL |
+----+------+
1 row in set (0.00 sec)

mysql> 
mysql> select * from foo;
+----+------+
| id | text |
+----+------+
|  1 | NULL |
+----+------+
1 row in set (0.00 sec)

fork時のrand()の挙動

perlでforkしたときに、srand呼ばないと親プロセスとseedの値が同じになってしまうので、子プロセスの間でrand値が変わらなくてハマる、という経験があったのだけど、rubyの場合だとそのようなことはないようだ。

# perl
use strict;
use warnings;

warn rand();

for ( 1..10 ) {
    fork()
        or next;
    warn "$$: " . rand();

    exit;
}

wait() for (1..10);
# ruby
rand()

10.times do 
  Process.fork do
    p [$$, rand()]
  end
end
Process.waitall

実行結果はこのような感じ

$ perl hoge.pl
0.272763864210905 at hoge.pl line 4.
61802: 0.107602762381674 at hoge.pl line 9.
61803: 0.107602762381674 at hoge.pl line 9.
61804: 0.107602762381674 at hoge.pl line 9.
61805: 0.107602762381674 at hoge.pl line 9.
61806: 0.107602762381674 at hoge.pl line 9.
61808: 0.107602762381674 at hoge.pl line 9.
61810: 0.107602762381674 at hoge.pl line 9.
61811: 0.107602762381674 at hoge.pl line 9.
61812: 0.107602762381674 at hoge.pl line 9.
61813: 0.107602762381674 at hoge.pl line 9.

$ ruby hoge.rb
[62124, 0.20627116612114527]
[62125, 0.333383116761385]
[62126, 0.23762467129788523]
[62127, 0.14602078653671557]
[62128, 0.5977205787316395]
[62129, 0.4148314943310857]
[62130, 0.05280638343461841]
[62131, 0.008602114330672928]
[62132, 0.3500050758109612]
[62133, 0.6088914568482737]

fluentdのログを調査するためのツールflgrepを書きました

おかげさまでRack::CommonLogger::Fluentを運用しはじめました。
Mongoに記録してごにょごにょとか、色々な方法があるらしいのですが、とりあえずファイルに記録しておいて、用途はあとで考えようという感じで使っています。

ということで、ログファイルを色々みてみてアクセスがどんな感じなのか、を調べたりするときに便利そうなflgrepというツールを作ってみました。(既にどこかにありそうな気もしつつ。Rack::CommonLogger::Fluentのv0.4.0から同梱されています。

Rack::CommonLogger::Fluentに入ってはいますが、fluentdのログ形式であれば使えるんではないかと思います。

$ flgrep
Usage: flgrep [options]
    -c, --column=s                   column name to target
    -v, --value=s                    value for filtering
    -d, --date[=s]                   filtering by date
        --until[=s]                  filtering by date until ...
        --since[=s]                  filtering by date since ...
    -m, --method=(regexp|gt|lt)      regexp (default): match by regexp, gt: greater than, lt: less than
        --mode=(filter|color)        filter (default): like grep, color: show colored line when match condition.

# 一秒以上かかったアクセスログを調べる
$ tail -f access.log | flgrep --column=runtime --method=gt --value=1.0

# 2012/06/06の20時以降のトップページへのアクセスを調べる
$ tail -f access.log | flgrep --column=path_info  --value=^/$ --since=2012-06-06T20:00

rackのアクセスログをfluentdへ投げるミドルウェア

railsのアクセスログは、とても機械的に解析しづらく、運用するにはあまりよろしくない。そこで、Rack::CommonLoggerを使ってアクセスログを別途とったりしようとしていたのですが、(一般的にはNew Rericとかをつかうのが普通なんでしょうか?)
ログのローテーションとか考え出すと、あまり良い方法が思いつかない、という関係で、

  • syslogへ投げる
  • fluentdへ投げる

というアイディアを思いついたのですが、syslogだと機械解析しづらいフォーマットになってしまったりあまり詳しい人が意外といなかったりする関係で、流行っているfluentdへ投げちゃえと思いRack::CommonLogger::Fluentというrackのミドルウェアを書いてみた次第。

https://github.com/walf443/rack-common_logger-fluent

まだ作り始めで、仕様をどうしようと悩んでいるのだけど、わりと需要はありそうな気がしていて、できるだけ使われるものにしたいのと、あとから後方互換性を崩すということはなるべくしたくないので、ご意見を募集中

ログのフォーマット

Rack:CommonLogger::Fluentでは、RackのリクエストからHashを構築し、それをFluent::Loggerを使い、fluentdへ投げる、という感じになっています。

  • "content_length": レスポンスのCONTENT_LENGTHヘッダの値。Integerにするか、Stringにするか悩んでいる。Intergerがよいのかな
  • "http_status": レスポンスのHTTP STATUS CODE。これもIntegerにするかStringにするか悩んでいる。Integerがよいのかな
  • "accessed_at": 日付はどのフォーマットの文字列にするべきか?
  • ヘッダが空だった場合の扱い。nil or ""で悩んでいる。どちらかというとnilかなー?

UNIXSocketに対応してないライブラリを無理矢理対応させる

プロトコルは同じなので、既にあるクライアントライブラリを使いたいのだけれど、そのクライアントライブラリが想定しているサーバーは、unixsocketには対応していない場合、クライアントライブラリにUnixSocketに対応させるpatchを送ってもacceptはされないでしょう。

とはいえ、patchを保持しつづけるのもしんどいので、うまくやれないかなーと思って作ってみた

https://github.com/walf443/unix_socket_hack

require 'unix_socket_hack'
UNIXSocketHack.apply({ 'unixsocket:9999' => '/path/to/unix.sock' })
sock = TCPSocket.new('unixsocket', 9999)
#=> UNIXSocket

# example using memcache-client.
require 'memcache'
memd = MemCache.new(['unixsocket:9999'])

unixsoket:9999へつなぎにいくと、UNIXSocketが返るようになるpatchをTCPSocketへ当てている。

もちろん、クライアントライブラリなどや、アプリケーションが実行する環境によっては相性が悪い可能性はありそうので、使わないにこしたことはないと思われる。

railsコマンドの実行を超早くするrrails

最近はお仕事で久々にがっつりrubyを書いてます。rails難しいです。

それはさておき、railsコマンドの実行が遅いのがつらいので、色々探していたら、jugyoさんのrails-shを見つけました。

rails-shはscalaのsbtとかみたいに、railsを読みこみ済みの環境を起動しっぱなしにしておき、既にライブラリ読みこみ状態からコンソールからコマンドを実行することで、ライブラリ読みこみにかかる時間を短縮する、というツールで、非常に実行を高速化することができます。

しばらく使っていたのですが、

  • raills-shのコンソールへ移動して打つのを忘れる
  • zshじゃない
  • RAILS_ENVがtestなコマンドも欲しいと2つのscreenのwindowを消費する
  • rake -Tが動かない

などがあったので、TCPでコマンドを送り、それ経由でrails/rakeコマンドを実行させるrrailsというやつを書いてみました。

使い方は簡単で、

bundle exec rrails start

とrrailsのサーバーを起動しておき、

rrails -- rails ....
rrails -- rake -T

などと、rails/rakeなどをそのまま送り実行します。

ずっとコマンドが動きっぱなしのrails console/severなどは、動かなかったりします。

guardからrails-serverを起動することでwindowを節約しつつ、development/testを一気に起動することができます。(guardの起動が遅くなるのが欠点ですが。。。

https://github.com/walf443/guard-rrails

bundle guard init rrails
bundle exec guard

既に似たようなものもあるような気もしますが、まずまず便利なのではないかと思います。

phpでTAP

phpでTAPやらperlのTest::Moreを移植しよう、というのは調べてみると、色々な人がやっているのだけど、subtestあたりに対応しているのはないっぽかったり個人的にあまり気にいったやつがなかったので書いた

         $t = new TestTAP();
         $t->ok(1==0, "failed test");
         $t->is(1, 0, "1 should be 0");
         $t->subtest("categorize test", function($t) {
             $t->ok(0==1, "It's subtest");
         });
         $t->done_testing();  # you should call at last.

subtestするためには、php 5.3以降でないといけないのが欠点かもしれない。

perlのTest::Moreはisとかokとかをグローバルに生やしているために複雑になってしまっているところはあるが、こんな感じでテスト用のインスタンスをつくってそれを元に実行する、という形式にすると、(書く側のおまじないは増えるが、)わりとsubtestの実装もすっきり書けるようだ。

新しい言語を覚えるときの題材として、TAPの実装はそこそこ簡単でよいかもしれない。