リンクするaprのバージョンを0.9xから1.2xに

WebDAV(=Apache)でリポジトリにアクセスすると、500が返ってきてログにも何も残らないと言う現象に遭遇する。で、色々調べていくうちのSubversionApacheaprのバージョンの不整合が原因と言う事が分かった。

なお、Apache HTTP Server 2.0系で利用されているAPR 0.9系と、Apache HTTP Server 2.2系で利用されているAPR 1.2系では、互換性がないことに注意されたい。

http://builder.japan.zdnet.com/news/story/0,3800079086,20361867,00.htm


トラブルシューティングの手順を残しておく。


まず、Apacheのログに何も残っていないので、telnetから直接WebDAVのリクエストを投げる。参考:Subversionによるバージョン管理 (2/3):Apache 2.0でWebDAV(後編) - @IT

$ telnet localhost 80
OPTIONS /svn/ HTTP/1.1
Host: hostname
Authorization: Basic ===============


すると、xmlでエラーの詳細が返ってくる。

HTTP/1.1 500 Internal Server Error
Date: Fri, 08 Feb 2008 02:43:28 GMT
Server: Apache
Content-Length: 290
Connection: close
Content-Type: text/xml; charset="utf-8"

<?xml version="1.0" encoding="utf-8"?>
<D:error xmlns:D="DAV:" xmlns:m="http://apache.org/dav/xmlns" xmlns:C="svn:">
<C:error/>
<m:human-readable errcode="22">
Can't set position pointer in file '/path/to/repos/db/revs/2406': Invalid argument
</m:human-readable>
</D:error>
Connection closed by foreign host.


ポイントは以下。

Can't set position pointer in file '/path/to/repos/db/revs/2406': Invalid argument


これでググるhttp://saikyoline.jp/wiki/index.php?%A5%E1%A5%E2%2FWebDAV%A4%C7Subversionaprのバージョンが原因と書いてある。

Subversionはそのままだと同梱のAPR(0.9.7)を使ってしまうので、Apache 2.2.0に同梱のAPR(1.0)を使わせるため「--with-apr」と「--with-apr-util」を指定する。
指定しなくてもコンパイルもできてApacheもしっかり起動するが、アクセスするときに

Can't set position pointer in file '/var/lib/svn-repos/db/revs/0': Invalid argument
みたいなこと言われてハマる

http://saikyoline.jp/wiki/index.php?%A5%E1%A5%E2%2FWebDAV%A4%C7Subversion

aprのバージョンを確認するにはlddを使う。まずはSubversion(mod_dav_svn)。

$ ldd /usr/local/apache2/modules/mod_dav_svn.so
        libaprutil-0.so.0 => /usr/lib/libaprutil-0.so.0 (0x008f9000)
        libapr-0.so.0 => /usr/lib/libapr-0.so.0 (0x00270000)
        (snip)

$ ll /usr/lib/libapr*.0
lrwxrwxrwx  1 root root 18  1月 25 14:57 /usr/lib/libapr-0.so.0 -> libapr-0.so.0.9.17
lrwxrwxrwx  1 root root 22  1月 25 14:57 /usr/lib/libaprutil-0.so.0 -> libaprutil-0.so.0.9.17


恐らく0.9.17。次にApache

$ ldd /usr/local/apache2/bin/httpd
        libaprutil-1.so.0 => /usr/local/apache228/lib/libaprutil-1.so.0 (0x0078a000)
        libapr-1.so.0 => /usr/local/apache228/lib/libapr-1.so.0 (0x00b5d000)


$ ll /usr/local/apache228/lib/libapr*.0
lrwxrwxrwx  1 root root 18  2月  6 16:03 /usr/local/apache228/lib/libapr-1.so.0 -> libapr-1.so.0.2.12
lrwxrwxrwx  1 root root 22  2月  6 16:03 /usr/local/apache228/lib/libaprutil-1.so.0 -> libaprutil-1.so.0.2.12


恐らく1.2.12。確かにバージョンが違う。
と言うわけで、再々度Subversionコンパイル。この際なので色々オプションを調べて自分なりのconfigureを作る事に。

./configure \
--with-apr=/usr/local/apache2 \
--with-apr-util=/usr/local/apache2 \
--with-apxs=/usr/local/apache2/bin/apxs \
--with-swig=/usr/local/bin/swig \
--with-neon=/usr/local/ \
PERL=/usr/local/bin/perl \
PYTHON=/usr/local/bin/python \
RUBY=/usr/local/bin/ruby \
--with-ssl \
--without-berkeley-db


これでインストールしたらWebDAVで無事アクセスできた。いい加減これで全部かなー。

sudoのちょっとしたトラブル

root以外のユーザを指定してsudoを実行しようとすると、実行を許可されていません、みたいなエラーが出た。

$ sudo -u foo ls /
Sorry, user hoge is not allowed to execute '/bin/ls /' as foo on host_name.

色々調べていると、sudoersファイル内でユーザ権限の設定をしていない事が分かった。

hoge ALL = (ALL) [NOPASSWD:] ALL
#一つ目のALLは接続元、二つ目の括弧付きALLはユーザ、三つ目はコマンドをそれぞれ表している。

二つ目の括弧付きを省略していると、ユーザはrootのみになる。sudo -lで自分の設定を確認できる。

$ sudo -l
User hoge may run the following commands on this host:
    (root) NOPASSWD: ALL

sudoersファイルの修正後。

$ sudo -l
User hoge may run the following commands on this host:
    (ALL) NOPASSWD: ALL


よく分からないまま使っているとトラブル発生時に困る例。

mongrel_clusterは複数バージョンが混合していると古いバージョンが使われる

開発環境にmongrel_clusterの1.0.5を入れてソースを眺めていたら、cluster::statusと言う新しいコマンドが増えていたのだけれど、リストに出てこなかったので、0.2.1をアンインストールしたら出てきた。
この辺りはgem_pluginの仕様なんだと思うけど、仕組みが謎。

全角スペース対応版strip

書いてみた。

速度的にはまだまだ改善の余地があると思う。

class String
  def strip_with_full_size_space
    s = " \s\v"
    self =~ /^[#{s}]+([^#{s}]+)[#{s}]+$/ ? $1 : self
  end
end


ベンチマーク用のコード。

require 'benchmark'
n = 50000
s1 = "   全半混在   "
Benchmark.bm do |x|
  x.report("s1") { n.times do s1.strip_with_full_size_space end }
  x.report("s1") { n.times do s1.strip end }
end


結果。

      user     system      total        real
s1  0.130000   0.000000   0.130000 (  0.122258)
s1  0.020000   0.000000   0.020000 (  0.027868)

正規表現のoオプションを外したバージョンの結果。

      user     system      total        real
s1  0.740000   0.010000   0.750000 (  0.739665)
s1  0.020000   0.000000   0.020000 (  0.027248)

oオプションの有無で5〜6倍近く実行速度に差が出る。

irbの起動時はLoadErrorが発生しても無視されるが、その後のライブラリの読み込み等は行われない

.irbrcで

require 'hogehoge'

とやっても例外も警告も出なかったので、なんでだろうと思って調べてみた。


irbの起動処理はirb/init.rb内で行われる。このうち、irbrcを読み込んでいるのはIRB.run_configメソッドだと思われる。以下にコードを抜粋する。

  def IRB.run_config
    if @CONF[:RC]
      begin
        load rc_file
      rescue LoadError, Errno::ENOENT
      rescue
        print "load error: #{rc_file}\n"
        print $!.class, ": ", $!, "\n"
        for err in $@[0, $@.size - 2]
          print "\t", err, "\n"
        end
      end
    end
  end


コードを見ると、.irbrcをrubyファイルとみなしてloadしている。つまり、途中で例外が発生した場合には以降の処理(require)は行われず、またそれを警告するメッセージ等も出力されない事になる。これはハマりそうだ…。
オーバーライドして置き換えてもいいのだけれど、まぁそんな事あまりないだろうから放置する事にする(と、皆考えるからずっと放置されている気がする)。

Subversionでhttp(s)のURLスキームを認識しない問題を解決する。

RetrospectivaのtrunkをGoogle Codeからcoしようとしたらエラーが出る。

$ svn co -q http://retrospectiva.googlecode.com/svn/trunk/ retrospectiva
svn: 'http://retrospectiva.googlecode.com/svn/trunk' 用の URL スキームを認識できません

色々調べてみたものの分からなかったので、エラーメッセージでググるとあっさり原因が見つかった。svnコマンドでhttp(s)系のリポジトリにアクセスする場合には、neonと言うライブラリが必要になるらしい。subversionのINSTALLファイルには以下のように書かれている。

4.  Neon library 0.25.x or 0.26.x (http://www.webdav.org/neon/)

The Neon library allows a Subversion client to interact with remote
repositories over the Internet via a WebDAV based protocol.  If you
want to use Subversion to connect to a server over ra_dav (via a
http:// or https:// url), you will require Neon.  (See also section
I.11 for information about "serf", an experimental alternative to
Neon for accessing servers over WebDAV.)

まず既存環境にNeonがあるか確認する。

$ rpm -qa | grep -i neon
neon-0.24.7-4

あった。でも要件を満たしていないようだ。と言うわけでNeonを含めてSubversionを再コンパイルする。NeonNeon公式からダウンロードするか、Subversionの依存ファイルを一括してパッケージしたtarファイルに含まれている。今回は後者を利用する。インストール方法も同様にINSTALLファイルに書かれているのでこれを参考に。

The source code is included with the Subversion dependencies package,
and it can also be obtained from:

  http://www.webdav.org/neon/neon-0.25.5.tar.gz
    -or-
  http://www.webdav.org/neon/neon-0.26.1.tar.gz

Building Neon inside the subversion build:

The Neon library source code can be placed in "./neon" if you
want Subversion to build it as part of the Subversion build process.

Unpack the archive using tar/gunzip.  Rename the resulting
directory from ./neon-0.XX.Y to just "./neon", inside the top
level of your Subversion source tree.  (This is what unpacking the
Subversion dependencies package does, too.)

手順は以下の通り。

$ wget http://subversion.tigris.org/downloads/subversion-1.4.6.tar.gz
$ tar zxf subversion-1.4.6.tar.gz
$ wget http://subversion.tigris.org/downloads/subversion-deps-1.4.6.tar.gz
$ tar zxf subversion-deps-1.4.6.tar.gz
$ cd subversion-1.4.6
$ ./configure
$ make
$ sudo make install

特に問題なく終了。configure時には以下の出力を確認した。

#
#configure: Configured to build neon 0.25.5:
#
#  Install prefix:  /usr/local
#  Compiler:        gcc
#  XML Parser:      expat
#  SSL library:     SSL support is not enabled
#  zlib support:    zlib support enabled, using -lz
#  Build libraries: Shared=no, Static=yes


再度チェックアウトし、無事チェックアウト出来る事を確認。

2008/1/29 追記

上記の手順でコンパイルした後、svkが動作しなくなったり、rubySubversionバインディングが動かなくなったりした。バインディングはバージョンが古いだけだったのだけれど、svkは原因がよく分からなかった。
以前とconfigureのオプションが違っていたりする事はあったのだが、Subvresionの依存パッケージにはNeon以外にapr,apr-util,zlibが入っていたので、それが問題になったのかもしれない。復旧した際にはNeon単体のみをソースツリーに含めるようにした。

2008/1/29 追記 その2

configureに--with-sslオプションを追加しないとhttpsでチェックアウトが出来なかったので、再度コンパイルし直し。

$ ./configure \
  PERL=/usr/local/bin/perl \
  PYTHON=/usr/local/bin/python \
  --with-swig=/usr/local/bin/swig \
  --with-ssl
$ make
$ sudo make install

コンパイル前。

$ svn --version
(snip)
以下のリポジトリアクセス (RA) モジュールが利用できます:

* ra_dav : WebDAV (DeltaV) プロトコルを使ってリポジトリにアクセスするモジュール。
  - 'http' スキームを操作します
* ra_svn : svn ネットワークプロトコルを使ってリポジトリにアクセスするモジュール。
  - 'svn' スキームを操作します
* ra_local : ローカルディスク上のリポジトリにアクセスするモジュール。
  - 'file' スキームを操作します


コンパイル後。

$ svn --version
(snip)
以下のリポジトリアクセス (RA) モジュールが利用できます:

* ra_dav : WebDAV (DeltaV) プロトコルを使ってリポジトリにアクセスするモジュール。
  - 'http' スキームを操作します
  - 'https' スキームを操作します
* ra_svn : svn ネットワークプロトコルを使ってリポジトリにアクセスするモジュール。
  - 'svn' スキームを操作します
* ra_local : ローカルディスク上のリポジトリにアクセスするモジュール。
  - 'file' スキームを操作します

ActiveRecordから生のクエリを実行する

ActiveRecord::Base.connection.execute(sql)

返ってくるのは生のResultSetなので、アプリ側で解釈する必要がある。以下の例ではMySQLの場合。

rs = ActiveRecord::Base.connection.execute("show processlist")
puts rs.class #=> Mysql::Result
rs.each{|row| p row}


あまり利用シーンはないが、update文などをRailsから実行させたいときに使える。