« 2008年02月 | メイン | 2008年04月 »

2008年03月31日

[Ruby] MechanizeでEOFError?

こんにちわ。ばたっちです。

WWW::Mechanizeでサイトを巡回していて、EOFErrorになることがあったので覚書。

エラーの内容は以下のとおり。


/usr/lib/ruby/1.8/net/protocol.rb:133:in `sysread': \
  end of file reached (EOFError)
        from /usr/lib/ruby/1.8/net/protocol.rb:133:in `rbuf_fill'
        from /usr/lib/ruby/1.8/timeout.rb:56:in `timeout'
        from /usr/lib/ruby/1.8/timeout.rb:76:in `timeout'
        from /usr/lib/ruby/1.8/net/protocol.rb:132:in `rbuf_fill'
        from /usr/lib/ruby/1.8/net/protocol.rb:116:in `readuntil'
        from /usr/lib/ruby/1.8/net/protocol.rb:126:in `readline'
        from /usr/lib/ruby/1.8/net/http.rb:2017:in `read_status_line'
        from /usr/lib/ruby/1.8/net/http.rb:2006:in `read_new'
        from /usr/lib/ruby/1.8/net/http.rb:1047:in `request'
        from /usr/lib/ruby/gems/1.8/gems/mechanize-0.6.10/lib/\
  mechanize.rb:514:in `fetch_page'
        from /usr/lib/ruby/gems/1.8/gems/mechanize-0.6.10/lib/\
  mechanize.rb:185:in `get'
        from test.rb:19
正確なパターンは分からないのですが、以下のようにJavaScriptファイルの取得前後でエラーになるようでした。
  • 「http://~/test1.html」のように最初のページをGETする
  • 相対パス指定で次のページをGETする (A)
  • 相対パス指定でJavaScriptをGETする
  • backメソッドで履歴を戻る
  • 相対パス指定で(A)のページなどを再GETする
特定の条件で発生するのか、一般的なサイトで同じ動作をさせても再現できなくて、具体的なサンプルを示せません。。orz

直接関係あるかは分かりませんが、気になった点は次のとおり。

  • サーバがIIS
  • Content-Typeが「application/x-javascript」(通常は text/javascript)
結論を言うと、Mechanize の keep_alive を無効にするとうまくようです。 ※サンプルコードはイメージです。

原因がはっきりしないので気持ち悪いですが、とりあえずうまく行きました。(^^)v
同じような現象に会った人いるかな?

2008年03月28日

[MySQL][Rails] Lost connection to MySQL server during query

こんにちわ。ばたっちです。

Railsのユニットテスト中にタイトルのエラーが出ることがあります。
調べてみたら、結構話題になってるみたいですね。

http://d.hatena.ne.jp/Rommy/20070814/p1
http://idm.s9.xrea.com/ratio/2006/04/08/000406.html


ユニットテストのエラーを見てると、
「Message: ActiveRecord::StatementInvalid: Mysql::Error: Lost connection to MySQL server during query」
と、
「Message: Errno::ECONNREFUSED: Connection refused - /tmp/mysql.sock」
が交互に出ている。

DB接続が切断(mysqld再起動?)→DB接続失敗を繰り返しているみたいですね。


MySQLのマニュアルを見てみると、どうやら MySQLクライアント―サーバ間で大きなサイズの
パケットが送られると接続が切れるそうです。

MySQL 4.1 リファレンスマニュアル :: A.2.9. Packet too large エラー
http://dev.mysql.com/doc/refman/4.1/ja/packet-too-large.html

my.cnfの [mysqld]セクションの max_allowed_packet を 1M→16Mに変更してみたらビンゴ。


[mysqld]
  :
max_allowed_packet = 16M
  :

/etc/init.d/mysql restart でMySQL再起動したら、ユニットテストが通るようになりました。
よかったよかった。A^^;


(追記)

一時的にうまくいっていたようで問題が再発。。orz

mysqld.logを見てみると「mysqld got signal 11」が始まるログが吐かれていて、
thd->query at 0xXXXXXXXX = DELETE FROM table1
というように、DELETE文実行時にエラーになっている模様。

MySQL+Senna(Tritonn)の全文検索を使っているのですが、下記のバージョンで修正された
「条件無しdeleteを実行するとインデックスのフラグ情報が欠けてしまう問題」というのが怪しそう。

http://qwik.jp/tritonn/ChangeLog.html#mysql-5_0_45-tritonn-1_0_7

MySQLのバージョンは変更できないので、条件なしDELETEにならない方法を考えることにしました。

エラーが発生しているのはユニットテストで、FULLTEXT INDEXを設定したテーブルに対する fixtureのロード中。
なので、test_helper.rbで fixtureロード中の DELETE文で WHERE句を追加するようにしてみました。


test/test_helper.rb
----
+class Fixtures < YAML::Omap
+  FULLTEXT_TABLES = ["table1"]
+  def delete_existing_fixtures
+    where = (FULLTEXT_TABLES.include?(@table_name)) ? "WHERE id>0" : ""
+    @connection.delete "DELETE FROM #{@table_name} #{where}", 'Fixture Delete'
+  end
+end

ファイルの最後に追加すればOKです。

FULLTEXT_TABLESに FULLTEXT INDEXを設定してあるテーブルを追加すれば、DELETE文に「WHERE id>0」を挿入します。
idフィールドがない(Railsで作ったテーブルなら通常はありますが)場合は、ここでテーブル毎の SQLを作るようにします。
(ちなみに「WHERE 1=1」みたいなのはダメみたい)

これでユニットテストのエラーが発生しなくなりました♪(^^)v