2012年9月4日火曜日

MySQLでvarcharカラムをintと比較するとおかしな結果になる

9月7日に真相(?)について追記しました。

自明のことだがSQLではWHERE句で指定した条件にマッチしたレコードのみを取得することが出来る。だが、MySQLでは文字列型のカラムに対して = 数値 という条件にすると、おもしろい(おかしな)結果になる。

対象テーブルの定義とデータ


以下のようにとてもシンプルなテーブルがある。

CREATE TABLE `string_table` (
`id` int(11) NOT NULL,
`value_string` varchar(1024) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

データはそれなりにたくさん入っている。

select count(*) from string_table;
→ 296,458行

クエリとその結果


まずは普通に文字列と完全一致で検索。

select * from string_table where value_string = 'aaa';


これは自明。
では次に0と完全一致で検索。予想では型が異なるのでSQLエラーになると思いきや、

select * from string_table where value_string = 0;

と、結果が取得出来る。それも件数を確認すると296,442行となっており、ほぼ全てのレコードが検索結果として出ていることが分かる。???なんだこれは。

ということで似たような条件で検索してみると…

select * from string_table where value_string = 1;


SQLはエラーにならず、結果は0件。

select * from string_table where value_string = 5;


SQLは当然エラーにならず、結果は3件。

select * from string_table where value_string like '5%';

で検索してみた結果と同一だったので、先頭が5の文字列のレコードがヒットしている。

結論


MySQLでは文字列型のカラムに対して条件を integer で指定すると、思わぬレコードが取得できてしまう。

なんでこうなるのかが気になるので色々と調べてみたが理由が分かっていない…。


真相


ブログを見てくれた方から情報をいただいた。


http://blog.usoinfo.info/article/180598548.html
解説はほぼ上記のブログで完結しているが、少しだけ補足する。

MySQLでは、左辺と右辺の型が異なる場合、どちらかに合わせて(変換して)比較が行われる。

文字列と文字列であれば変換は発生しないので意図した通りの結果になるが、今回は左辺が文字列で右辺が数値になっている。この場合、MySQLでは左辺と右辺の両方を数値に変換してから比較を行う。今回のケースでは左辺には既存のレコードの値が入ってくるが、数字になり得ないもの( aaaaa, bbbbb, aa033e など)が来た場合は全て0に変換されてしまう。

今回の検索結果の1行目を例にすると、

select * from string_table where value_string = 0;

↓ (1行目のデータが入ったとすると)

select * from string_table where 'sc167' = 0;

↓ (sc167は数字になり得ないので0に変換される)

select * from string_table where 0 = 0;

この時、 where句は true となるためこのレコードが抽出結果として出てくる。

なお、=5 の時は先頭が5となっている文字列が全て抽出されている。
どうやらMySQLは先頭が数字で始まっている文字列は数字が終わるまでの部分で数値に変換してくれるようだ。

とてもすっきりした。

情報をくださった方、ありがとうございました。

補足


そもそも、こんなことが起きたのは Prepared Statement を使用して php から MySQL のデータを取得していたのだが、埋め込むパラメータを変数で渡していたのだが、明示的に文字列にキャスト等をしておらず、文字列が全て数字で構成されていた場合に文字列ではなく数値と扱われてしまい、最終的に生成されたSQLが = 0 というようになってしまって今回の現象が起きた。

ちなみに Oracle で = 0 を試したところ結果は0件だった。SQLの仕様ではなさそうだが真相はよく分かっていない。
ソースを読めば分かるけどね…。

2012年8月15日水曜日

vert.x を使ってみた

vert.x なるものが軽量でとても良いと聞いたので、使ってみることにした。

vert.x とは


アプリケーションフレームワーク(サーバー)である。

公式である http://vertx.io/ を見て特徴をかいつまむとこんな感じ。

  • Polyglot
    複数の言語が使えるということ。Javascript, Coffee Script, Ruby, Python, Groovy, Javaなど。
    なおかつ、どれか1つではなくミックスして使うことが可能
  • Simplicity
    シンプル。必要なコードだけ書けば動く。設定の XML ファイルなどはない
  • Scalability
    拡張可能。メッセージ渡し、変更されない共有データによって最適化される。
  • Concurrency
    並列処理。超シンプルな並列処理の仕組みがあり、いわゆるマルチスレッドプログラミングの難しさに悩むこともない
  • WebSocketのサポート

インストール


今回は Ubuntu にインストールしてみる。

まずは vert.x をダウンロード

wget http://vertx.io/downloads/vert.x-1.2.3.final.zip

ここ http://vertx.io/install.html を見ると、必要なコンポーネントに JDK7 が必要ということなので、

sudo apt-get install openjdk-7-jdk でインストール

vert.x のインストールはファイルを解凍してパスを通せば良いようだ。

ということで、 vi ~/.profile で以下を追加

# set vert.x path to PATH
if [ -d "/usr/local/bin/vertx" ]; then
    PATH="/usr/local/bin/vertx/vert.x-1.2.3.final/bin:$PATH"
fi

追加しただけでは反映されないので、

source ~/.profile

で、即時反映。

ということで vert.x が動くかどうか確認

vertx version
と打ったら
vert.x-1.2.3.final
と表示されたのでOK

動作確認


とりあえず javascript で動かしてみる。

適当な場所に first_server.js としてファイルを作成し、以下の内容を記載。

load('vertx.js');

vertx.createHttpServer().requestHandler(function(req) {
    req.response.end("Hello vert.x world.");
}).listen(8080, 'localhost');

その後、以下のコマンドを実行。

vertx run first_server.js

ブラウザで http://localhost:8080/ にアクセスすると意図通りに表示された。




感想的なもの



  • インストールは簡単
  • javascript のソースを書いたが、至極シンプル。
  • 今後はサポートされている他の言語も使ってみようかと思っている(Java, Ruby, Pythonなど)

2012年4月9日月曜日

rsync の include 使用方法(include と exclude の整理)

rsync コマンドを include オプションを指定して実行しようとしたがうまくいかなかったが、ネットをあれこれ検索していたらうまくいったのでその結果をまとめる。

その前に include と exclude オプションとは

rsync コマンドには --include オプションと --exclude オプションがある。それぞれの意味は以下の通り。
  • --include : --include="hogehoge" で指定した文字列にマッチするファイルまたはディレクトリが同期対象となる
  • --exclude : --exclude="hogehoge" で指定した文字列にマッチするファイルまたはディレクトリが同期対象から除外される
つまり /var の中の httpd.log のみを同期することや /var の中の httpd.log 以外を同期するといったことが可能となる。

じゃあやってみよう


ということで以下のようなディレクトリとファイルがあるとする。

同期対象のディレクトリ構成



これに対して以下のコマンドを実行する。
rsync -av --delete --include="config" rsync_test/ to_dir01
予想では rsync_test/config のみが to_dir01 に作成されるはずだ。だが、実際は…


となり、すべて同期されてしまう。
ここで、 man などでヘルプを見てみて--excludeと一緒に指定しないと駄目そうと判明したので以下のように実行してみた。デフォルト全て除外でconfigだけ対象という意図だ。
rsync -av --delete --exclude="*" --include="config" rsync_test/ to_dir02
これで実行した結果が以下のようになる。


きちんと指定しているにも関わらずすべて対象外となり何も同期されない。この時、
  • 指定方法がフルパスじゃないと駄目?
  • 対象がディレクトリだから最後に / がないと駄目?
という試行錯誤を小一時間繰り返したが解決しない…。

include と exclude オプションは順序が大事


man を見ても分からないのでGoogle先生に聞いてみた。そしたら以下のサイトが見つかった。


まさに今回の件にドンピシャ。ということで以下のように変更してみた。
rsync -av --delete --include="config" --exclude="*" rsync_test/ to_dir03
これで実行した結果がこちら。


うまくいった。結局は使い方が間違っていたということか。

にしても今までコマンドをいろいろ使ってきたけどコマンドラインオプションに順序性があることがあるということを初めて知った。なんとなく違和感があるけどそういうものなのか…


include と exclude の指定方法による実行結果への影響

ということでうまくいったのでいろんなパターンを試してみる。

1.includeで直下のファイル名を指定した場合

rsync -av --delete --include="Rakefile" --exclude="*" rsync_test/ p01

対象のファイルのみが同期され、それ以外のファイルディレクトリ、ファイルは除外された。

2.includeで直下のディレクトリ名を指定した場合

rsync -av --delete --include="app" --exclude="*" rsync_test/ p02

対象のディレクトリのみが同期され、そのディレクトリ内のファイル、ディレクトリ、およびそれ以外のファイル、ディレクトリは除外された。

3.includeで直下でないディレクトリ名を指定した場合(失敗)

rsync -av --delete --include="controllers" --exclude="*" rsync_test/ p03

controllersという名前のディレクトリは存在していますが、パスの指定方法は相対パス指定であるため、今回の指定方法の場合はrsync_test/controllersというディレクトリを指定していることになり、そのようなディレクトリはないため何も同期されないという結果になる。

4.includeで直下でないディレクトリ名を正しく指定した場合(失敗)

rsync -av --delete --include="app/controllers" --exclude="*" rsync_test/ p04

今回は正しく指定しているにも関わらず、対象のディレクトリが作成されていない。つまり、この指定方法ではうまくいかない。原因は後回しにし次のパターンへ。

5.includeで直下でないディレクトリ名と対象ディレクトリの親ディレクトリを同時に指定した場合

rsync -av --delete --include="app" --include="app/controllers" --exclude="*" rsync_test/ p05

今回はうまくいった。つまりサブディレクトリを指定したい場合は、対象のディレクトリに至るまでの全ての親ディレクトリを--includeで指定する必要があるということだ。

6.includeでディレクトリ名/*を指定した場合(失敗ケース)

rsync -av --delete --include="app" --include="app/controllers/*" --exclude="*" rsync_test/ p06

appまでは作成されたがapp/controllers以降が作成されなかった。これはapp/controllers/*はあくまで指定したディレクトリに存在するファイルやサブディレクトリを対象としているのであり、その時点でapp/controllersディレクトリが対象に含まれていないためにapp/controllersディレクトリが作成されないためファイルやサブディレクトリが作成されない。つまり、は次のように指定する必要がある。

7.includeでディレクトリ名/*を指定した場合

rsync -av --delete --include="app" --include="app/controllers" --include="app/controllers/*" --exclude="*" rsync_test/ p07

このように実行すれば目的の動作となる。これはなんとかならないものか…。


8.includeでディレクトリ名/**を指定した場合

rsync -av --delete --include="app" --include="app/controllers" --include="app/controllers/**" --exclude="*" rsync_test/ p08

指定したディレクトリに含まれるファイルとサブディレクトリ、そのサブディレクトリに含まれるファイルやサブディレクトリ全てが同期される。

まとめ

includeについてはひとまずこれで終了。まとめると以下のようになる。
  1. include で特定のディレクトリやファイルを指定したい場合は include を先に書いて --exclude="*" を後に指定しなければならない
  2. include でファイルやディレクトリを指定する場合は相対パスで指定しなければならない
    パターン3を参照
  3. include でサブディレクトリやサブディレクトリにあるファイルを指定する場合は、対象のファイルまたはディレクトリに至るまでのディレクトリをすべて--includeで指定しなければならない
    パターン5を参照
  4. include でディレクトリ以下のファイルを指定したい場合は対象のディレクトリ対象のディレクトリ内のファイル(ディレクトリ)は別々に --include で指定しなければならない
    → パターン7,8 を参照

余談

excludeはまとめる予定は今のところなし…。

2012年1月4日水曜日

JBoss4.2にMySQLに接続するためのDatasourceを作ってS2JDBCで接続する

JBoss の Datasource を経由して MySQL に接続してみた。

1.準備

JBoss は 4.2.3GA を準備。
MySQL は 5.1.58 community を準備。

インストール後に以下の手順が必要になります。

1.1. データベーススキーマの作成

MySQLに任意の名前でスキーマを作成します。既に存在していればそれを使用しても構いません。
詳細は省略します。

1.2. MySQL用のJDBCドライバーを配置

ここ( MySQL :: Download Connector/J )から対応するJDBCドライバーをダウンロードします。
その後、 mysql-connector-java-5.1.6.jar を以下のディレクトリに配置します。

{JBossのインストールディレクトリ}/server/default/lib/

2.Datasource の定義ファイルを作成

2.1. ファイルの作成

以下のようにファイルを作成する(中身は空で良い)

ディレクトリ:{JBossのインストールディレクトリ}/server/default/deploy
ファイル名:{任意の名前}-ds.xml

ちなみに自分はここでファイル名を ds-hogehoge.xml としてしまったために、
JBossが以下のように表示するだけで全く読み取ってくれなくて悩んでたのは秘密。
いやー、ホント悩んだ。

--- Incompletely deployed packages ---
org.jboss.deployment.DeploymentInfo@13376b0a { url=file:/C:/Servers/jbos-4.2.3.GA/server/default/deploy/ds-hogehoge.xml }
deployer: null
status: null
state: INIT_WAITING_DEPLOYER
watch: file:/C:/Servers/jboss-4.2.3.GA/server/default/deploy/ds-hogehoge.xml
altDD: null
lastDeployed: 1325039984621
lastModified: 1325039984621
mbeans:

ちなみにこちらのサイトの情報でファイル名の問題に気づきました。

2.2. Datasource の定義を記述

2.1.で作成したファイルに以下のように記述する。


  
    JNDI名
    jdbc:mysql://ホスト名:ポート番号/スキーマ名?useUnicode=true&
    characterEncoding=UTF-8
    
    com.mysql.jdbc.Driver
    ユーザー名
    パスワード
    org.jboss.resource.adapter.jdbc.vendor.
     MySQLExceptionSorter
    8
    8
    0

    
    
       JNDI名
    
  


3.JBoss に Datasource が登録されているか確認

この状態で JBoss を起動。以下のように表示されJNDIに登録されればとりあえず成功です。

15:21:23,170 INFO  [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=JNDI名' to JNDI name 'java:JNDI名'
15:21:23,189 INFO  [TomcatDeployer] deploy, ctxPath=/jmx-console, warUrl=.../deploy/jmx-console.war/
15:21:23,316 INFO  [Http11Protocol] Coyote HTTP/1.1を http-127.0.0.1-8080 で起動します
15:21:23,330 INFO  [AjpProtocol] Starting Coyote AJP/1.3 on ajp-127.0.0.1-8009
15:21:23,340 INFO  [Server] JBoss (MX MicroKernel) [4.2.3.GA (build: SVNTag=JBoss_4_2_3_GA date=200807181417)] Started in 7s:379ms

4.S2JDBC経由でデータベースにアクセス

登録したJNDIを使ってデータベースに接続するにはアプリケーションの jdbc.dicon を以下のように記述。


  

  
  
    
      
    
    100
  

  
        @org.seasar.extension.j2ee.JndiResourceLocator@lookup("java:データソース名")
  



自分はこれで出来たが、諸事情により実行結果の画面やログは省略させてもらいます。

今回参考にさせていただいたサイト

JBossにMySQL用のデータソースを定義する方法について参考にさせていただいた
JBoss/MySQLと接続する / http://www.masatom.in/pukiwiki/JBoss/MySQL%A4%C8%C0%DC%C2%B3%A4%B9%A4%EB/

S2JDBCでJBossのデータソースを利用する方法について参考にさせていただいた
APサーバのコネクションプールを使う : JDBCの設定 / http://s2container.seasar.org/2.4/ja/jdbc.html#GenericApplicationServer

途中でそもそもJBossって何だ?ってなった時に参考にさせていただいた(JBossってTomcatを含んでるものなのね。そりゃtomcatより起動とか遅いわけだ…
TomcatとJBossの違い - OpenGroove : http://open-groove.net/tomcat/difference-tomcat-jboss/

2011年11月1日火曜日

rsync実行中にエラー(ネットワークがダウンなど)が発生すると途中までの処理結果が反映されてしまう

rsync を使う場合、大量のファイルの同期を取ることが多い。
では、 rsync を実行している最中にネットワークが切れるなどした場合、途中まで転送されたファイルがどうなるのかを検証した。

2011/11/01 --temp-dir と --compare-dest についての追加検証を追記しました。

実行環境

まず、実行環境は以下の通り。

転送元マシン
OS:Ubuntu10(VMWare上の仮想マシン)
IP:192.168.119.144

転送先マシン
OS:CentOS6(VMWare上の仮想マシン)

転送対象ファイル
Java6のJavadoc(http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u25-doc-download-355137.html
サイズは 264MB ある

rsync実行時のコマンド
rsync -av --delete --progress -e ssh --bwlimit=128 --timeout=30 ./javadoc/ username@192.168.119.144:/home/username/to_dir/lowspeed
※普通に実行すると高速過ぎるので --bwlimit で転送速度を低くし、なおかつタイムアウトを30秒に設定

ネットワークエラーの発生方法

ネットワークエラーは通常では起き得ないため以下の方法で人為的に発生させた。

転送元マシンにログインし、以下のコマンドを実行(root)

service network stop

こうすることにより転送元と転送先の間のネットワークがダウンしたことになる。

検証結果

新規ファイル転送の場合

rsync の出力は以下の通り。「docs/api/index-files/index-4.html」の転送に失敗して終了している。

~$ rsync -av --delete --progress -e ssh --bwlimit=128 --timeout=30 ./javadoc/ username@192.168.119.144:/home/username/to_dir/lowspeed
username@192.168.119.144's password: 
sending incremental file list
./
docs/
docs/index.html
       39679 100%   53.99kB/s    0:00:00 (xfer#1, to-check=1022/1025)
docs/api/
docs/api/allclasses-frame.html
      503784 100%  122.02kB/s    0:00:04 (xfer#2, to-check=1052/1063)
docs/api/allclasses-noframe.html
      427924 100%  100.29kB/s    0:00:04 (xfer#3, to-check=1051/1063)
docs/api/constant-values.html
     1267915 100%  118.30kB/s    0:00:10 (xfer#4, to-check=1050/1063)
docs/api/deprecated-list.html
      167204 100%  117.90kB/s    0:00:01 (xfer#5, to-check=1049/1063)
docs/api/help-doc.html
       10463 100%   27.10kB/s    0:00:00 (xfer#6, to-check=1048/1063)
docs/api/index.html
        1399 100%    3.62kB/s    0:00:00 (xfer#7, to-check=1047/1063)
docs/api/overview-frame.html
       28532 100%   44.30kB/s    0:00:00 (xfer#8, to-check=1046/1063)
docs/api/overview-summary.html
       60507 100%   52.15kB/s    0:00:01 (xfer#9, to-check=1045/1063)
docs/api/overview-tree.html
     1114950 100%  127.05kB/s    0:00:08 (xfer#10, to-check=1044/1063)
docs/api/package-list
        4115 100%    7.99kB/s    0:00:00 (xfer#11, to-check=1043/1063)
docs/api/serialized-form.html
     1499294 100%  120.73kB/s    0:00:12 (xfer#12, to-check=1042/1063)
docs/api/stylesheet.css
        1391 100%    1.33MB/s    0:00:00 (xfer#13, to-check=1041/1063)
docs/api/index-files/
docs/api/index-files/index-1.html
      749599 100%  126.32kB/s    0:00:05 (xfer#14, to-check=1035/1063)
docs/api/index-files/index-10.html
      258969 100%   95.51kB/s    0:00:02 (xfer#15, to-check=1034/1063)
docs/api/index-files/index-11.html
       84952 100%   73.22kB/s    0:00:01 (xfer#16, to-check=1033/1063)
docs/api/index-files/index-12.html
      269850 100%  130.59kB/s    0:00:02 (xfer#17, to-check=1032/1063)
docs/api/index-files/index-13.html
      462569 100%  127.97kB/s    0:00:03 (xfer#18, to-check=1031/1063)
docs/api/index-files/index-14.html
      338993 100%  105.03kB/s    0:00:03 (xfer#19, to-check=1030/1063)
docs/api/index-files/index-15.html
      186915 100%  120.41kB/s    0:00:01 (xfer#20, to-check=1029/1063)
docs/api/index-files/index-16.html
      773522 100%  117.32kB/s    0:00:06 (xfer#21, to-check=1028/1063)
docs/api/index-files/index-17.html
       25326 100%   97.37kB/s    0:00:00 (xfer#22, to-check=1027/1063)
docs/api/index-files/index-18.html
      815983 100%  121.62kB/s    0:00:06 (xfer#23, to-check=1026/1063)
docs/api/index-files/index-19.html
     1853449 100%  122.58kB/s    0:00:14 (xfer#24, to-check=1025/1063)
docs/api/index-files/index-2.html
      361451 100%  103.73kB/s    0:00:03 (xfer#25, to-check=1024/1063)
docs/api/index-files/index-20.html
      588362 100%  119.90kB/s    0:00:04 (xfer#26, to-check=1023/1063)
docs/api/index-files/index-21.html
      345112 100%   99.04kB/s    0:00:03 (xfer#27, to-check=1022/1063)
docs/api/index-files/index-22.html
      289076 100%  117.77kB/s    0:00:02 (xfer#28, to-check=1021/1063)
docs/api/index-files/index-23.html
      309110 100%  109.21kB/s    0:00:02 (xfer#29, to-check=1020/1063)
docs/api/index-files/index-24.html
       90439 100%   64.00kB/s    0:00:01 (xfer#30, to-check=1019/1063)
docs/api/index-files/index-25.html
       22429 100%   58.25kB/s    0:00:00 (xfer#31, to-check=1018/1063)
docs/api/index-files/index-26.html
       13692 100%   26.64kB/s    0:00:00 (xfer#32, to-check=1017/1063)
docs/api/index-files/index-27.html
      147169 100%   88.06kB/s    0:00:01 (xfer#33, to-check=1016/1063)
docs/api/index-files/index-3.html
     1135603 100%  119.21kB/s    0:00:09 (xfer#34, to-check=1015/1063)
docs/api/index-files/index-4.html
       32768   6%   84.66kB/s    0:00:05  
[sender] io timeout after 30 seconds -- exiting
rsync error: timeout in data send/receive (code 30) at io.c(140) [sender=3.0.8]

そして、転送先のディレクトリを見ると、

$ pwd
/home/username/to_dir/lowspeed/docs/api/index-files
$ ls | more
index-1.html
index-10.html
index-11.html
index-12.html
index-13.html
index-14.html
index-15.html
index-16.html
index-17.html
index-18.html
index-19.html
index-2.html
index-20.html
index-21.html

rsync のログでは index-3.html までが 100% となっているが、実際には index-21.html までしか転送されていない

更新の場合

次に既にファイルが存在している状態で、rsync で更新した場合はどうなるかを見る。

エラー発生時付近のrsync のログは以下の通り。

docs/api/java/awt/Scrollbar.html
       98791 100%  421.29kB/s    0:00:00 (xfer#191, to-check=1115/1334)
docs/api/java/awt/Shape.html
       35590 100%  151.77kB/s    0:00:00 (xfer#192, to-check=1114/1334)
docs/api/java/awt/SplashScreen.html
       24392 100%  100.09kB/s    0:00:00 (xfer#193, to-check=1113/1334)
docs/api/java/awt/Stroke.html
       11295 100%   46.35kB/s    0:00:00 (xfer#194, to-check=1112/1334)
docs/api/java/awt/SystemColor.html
       70817 100%  289.36kB/s    0:00:00 (xfer#195, to-check=1111/1334)
docs/api/java/awt/SystemTray.html
       33103 100%  134.70kB/s    0:00:00 (xfer#196, to-check=1110/1334)
docs/api/java/awt/TextArea.AccessibleAWTTextArea.html
       29641 100%  120.11kB/s    0:00:00 (xfer#197, to-check=1109/1334)
docs/api/java/awt/TextArea.html
       78476 100%  316.68kB/s    0:00:00 (xfer#198, to-check=1108/1334)
docs/api/java/awt/TextComponent.AccessibleAWTTextComponent.html
       47309 100%  184.80kB/s    0:00:00 (xfer#199, to-check=1107/1334)
docs/api/java/awt/TextComponent.html
       76140 100%  296.24kB/s    0:00:00 (xfer#200, to-check=1106/1334)
docs/api/java/awt/TextField.AccessibleAWTTextField.html
       29680 100%  115.02kB/s    0:00:00 (xfer#201, to-check=1105/1334)
docs/api/java/awt/TextField.html
       79300 100%  306.09kB/s    0:00:00 (xfer#202, to-check=1104/1334)
docs/api/java/awt/TexturePaint.html
       20813 100%   80.34kB/s    0:00:00 (xfer#203, to-check=1103/1334)
docs/api/java/awt/Toolkit.html
      171298 100%  658.60kB/s    0:00:00 (xfer#204, to-check=1102/1334)
docs/api/java/awt/Transparency.html
       13861 100%   53.08kB/s    0:00:00 (xfer#205, to-check=1101/1334)
docs/api/java/awt/TrayIcon.MessageType.html
       17184 100%   65.81kB/s    0:00:00 (xfer#206, to-check=1100/1334)
docs/api/java/awt/TrayIcon.html
       54942 100%  209.59kB/s    0:00:00 (xfer#207, to-check=1099/1334)
docs/api/java/awt/Window.AccessibleAWTWindow.html
       29854 100%  113.88kB/s    0:00:00 (xfer#208, to-check=1098/1334)
[sender] io timeout after 30 seconds -- exiting
rsync error: timeout in data send/receive (code 30) at io.c(140) [sender=3.0.8]


そして転送先のファイルは以下の通り。

11月  1 03:34 2011 SystemTray.html
11月  1 03:34 2011 TextArea.AccessibleAWTTextArea.html
11月  1 03:34 2011 TextArea.html
11月  1 03:34 2011 TextComponent.AccessibleAWTTextComponent.html
11月  1 03:34 2011 TextComponent.html
11月  1 03:34 2011 TextField.AccessibleAWTTextField.html
11月  1 03:34 2011 TextField.html
11月  1 03:34 2011 TexturePaint.html
11月  1 03:34 2011 Toolkit.html
11月  1 03:34 2011 Transparency.html
11月  1 03:34 2011 TrayIcon.MessageType.html
11月  1 03:34 2011 TrayIcon.html
11月  1 03:34 2011 Window.AccessibleAWTWindow.html
11月  1 03:21 2011 Window.html
11月  1 03:21 2011 package-frame.html
11月  1 03:21 2011 package-summary.html
11月  1 03:21 2011 package-tree.html
11月  1 03:21 2011 package-use.html

更新においてはWindow.AccessibleAWTWindow.htmlの後で処理がエラーとなったため、それ以降のファイルに日付は古いまま(未更新)となっている。


削除の場合

削除の場合も本来は検証すべきだが、眠くて限界なのでおそらく同じ挙動であることが予想されるため省略。


まとめ

rsync を使ってファイル同期を実施した場合、以下のことが発生する可能性がゼロでない。

  1. 処理途中でネットワーク障害が発生した場合はそこまでの新規作成、更新、削除が反映された状態となる
  2. 同期対象のファイルが大量であったり、サイズが大きかったりする場合は、エラーが発生していない状態であっても、転送中にアクセスされた場合は全体として見た場合に中途半端となる可能性がある(例えばリンク元だけ更新されてリンク先は未更新でリンク切れとなる)
  3. コミット単位はファイルである(これは予想)
つまり、何かの運用で使用しようとするのであれば、上記が発生する可能性があることを考慮する必要がある。

はー、すっきりした。

追加検証

--temp-dir と --compare-dest というオプションが存在するという情報を得たので再度検証。
それぞれの説明はこちらを参照。

なお、転送先にはファイルがない状態、つまり全て追加となるモードで実施した。

まず、準備として転送先にテンポラリー用のディレクトリとして以下を作成

/home/username/to_dir/tmp

--temp-dir を使用した場合

rsync実行時のログは以下の通り。

$ rsync -av --delete --progress -e ssh --bwlimit=128 --timeout=30 --temp-dir=/home/username/to_dir/tmp ./javadoc/ username@192.168.119.144:/home/username/to_dir/lowspeed
username@192.168.119.144's password:
sending incremental file list
./
docs/
docs/index.html
       39679 100%    6.59MB/s    0:00:00 (xfer#1, to-check=1022/1025)
docs/api/
docs/api/allclasses-frame.html
      503784 100%  125.76kB/s    0:00:03 (xfer#2, to-check=1052/1063)
docs/api/allclasses-noframe.html
      427924 100%  103.57kB/s    0:00:04 (xfer#3, to-check=1051/1063)
docs/api/constant-values.html
     1267915 100%  116.98kB/s    0:00:10 (xfer#4, to-check=1050/1063)
docs/api/deprecated-list.html
      167204 100%  117.64kB/s    0:00:01 (xfer#5, to-check=1049/1063)
docs/api/help-doc.html
       10463 100%   27.10kB/s    0:00:00 (xfer#6, to-check=1048/1063)
docs/api/index.html
        1399 100%    3.62kB/s    0:00:00 (xfer#7, to-check=1047/1063)
docs/api/overview-frame.html
       28532 100%   44.30kB/s    0:00:00 (xfer#8, to-check=1046/1063)
docs/api/overview-summary.html
       60507 100%   58.62kB/s    0:00:01 (xfer#9, to-check=1045/1063)
docs/api/overview-tree.html
     1114950 100%  127.12kB/s    0:00:08 (xfer#10, to-check=1044/1063)
docs/api/package-list
        4115 100%    6.38kB/s    0:00:00 (xfer#11, to-check=1043/1063)
docs/api/serialized-form.html
     1499294 100%  121.01kB/s    0:00:12 (xfer#12, to-check=1042/1063)
docs/api/stylesheet.css
        1391 100%    1.37kB/s    0:00:00 (xfer#13, to-check=1041/1063)
docs/api/index-files/
docs/api/index-files/index-1.html
      749599 100%  108.13kB/s    0:00:06 (xfer#14, to-check=1035/1063)
docs/api/index-files/index-10.html
      258969 100%  117.85kB/s    0:00:02 (xfer#15, to-check=1034/1063)
docs/api/index-files/index-11.html
       84952 100%   94.06kB/s    0:00:00 (xfer#16, to-check=1033/1063)
docs/api/index-files/index-12.html
      269850 100%   90.84kB/s    0:00:02 (xfer#17, to-check=1032/1063)
docs/api/index-files/index-13.html
      462569 100%  112.04kB/s    0:00:04 (xfer#18, to-check=1031/1063)
docs/api/index-files/index-14.html
      338993 100%  125.16kB/s    0:00:02 (xfer#19, to-check=1030/1063)
docs/api/index-files/index-15.html
      186915 100%   90.54kB/s    0:00:02 (xfer#20, to-check=1029/1063)
docs/api/index-files/index-16.html
      773522 100%  109.00kB/s    0:00:06 (xfer#21, to-check=1028/1063)
docs/api/index-files/index-17.html
       25326 100%   49.17kB/s    0:00:00 (xfer#22, to-check=1027/1063)
docs/api/index-files/index-18.html
      815983 100%  117.36kB/s    0:00:06 (xfer#23, to-check=1026/1063)
docs/api/index-files/index-19.html
      589824  31%  127.05kB/s    0:00:09 
[sender] io timeout after 30 seconds -- exiting
rsync error: timeout in data send/receive (code 30) at io.c(140) [sender=3.0.8]

転送先のディレクトリを見ると以下のようになっている。

$ pwd
/home/username/to_dir/lowspeed/docs/api/index-files
$ ls | more
index-1.html
index-10.html
index-11.html
index-12.html
index-13.html
index-14.html
index-15.html

と、結果はオプションをつける前と変わらなかった。

ただし、実行中にテンポラリーディレクトリの中身を見たところ以下のように表示された。

$ pwd
/home/username/to_dir/tmp
$ ls -a | more
.
..
.serialized-form.html.ly3MAT
$ ls -a | more
.
..
.index-1.html.qXT1Ll
$ ls -a | more
.
..
.index-10.html.ZddjTQ

転送対象のファイルのテンポラリーが作成されていることがわかった。
だが、一度に作成されるテンポラリーファイルは上記の通り1つのみであり、
やはりファイル単位でのテンポラリー作成→本来のファイルに置き換えとなっているようだ。


--compare-dest を使用した場合

rsync実行時のログは以下の通り。
$ rsync -avI --delete --progress -e ssh --bwlimit=128 --timeout=30 --compare-dest=/home/username/to_dir/tmp ./javadoc/ username@192.168.119.144:/home/username/to_dir/lowspeed
username@192.168.119.144's password: 
sending incremental file list
./
docs/
docs/index.html
       39679 100%    6.59MB/s    0:00:00 (xfer#1, to-check=1022/1025)
docs/api/
docs/api/allclasses-frame.html
      503784 100%  125.18kB/s    0:00:03 (xfer#2, to-check=1052/1063)
docs/api/allclasses-noframe.html
      427924 100%  103.39kB/s    0:00:04 (xfer#3, to-check=1051/1063)
docs/api/constant-values.html
     1267915 100%  116.80kB/s    0:00:10 (xfer#4, to-check=1050/1063)
docs/api/deprecated-list.html
      167204 100%  117.73kB/s    0:00:01 (xfer#5, to-check=1049/1063)
docs/api/help-doc.html
       10463 100%   27.03kB/s    0:00:00 (xfer#6, to-check=1048/1063)
docs/api/index.html
        1399 100%    3.61kB/s    0:00:00 (xfer#7, to-check=1047/1063)
docs/api/overview-frame.html
       28532 100%   44.16kB/s    0:00:00 (xfer#8, to-check=1046/1063)
docs/api/overview-summary.html
       60507 100%   58.39kB/s    0:00:01 (xfer#9, to-check=1045/1063)
docs/api/overview-tree.html
     1114950 100%  126.99kB/s    0:00:08 (xfer#10, to-check=1044/1063)
docs/api/package-list
        4115 100%    6.38kB/s    0:00:00 (xfer#11, to-check=1043/1063)
docs/api/serialized-form.html
     1499294 100%  120.94kB/s    0:00:12 (xfer#12, to-check=1042/1063)
docs/api/stylesheet.css
        1391 100%    1.37kB/s    0:00:00 (xfer#13, to-check=1041/1063)
docs/api/index-files/
docs/api/index-files/index-1.html
      749599 100%  107.95kB/s    0:00:06 (xfer#14, to-check=1035/1063)
docs/api/index-files/index-10.html
      258969 100%  117.85kB/s    0:00:02 (xfer#15, to-check=1034/1063)
docs/api/index-files/index-11.html
       84952 100%   94.06kB/s    0:00:00 (xfer#16, to-check=1033/1063)
docs/api/index-files/index-12.html
      269850 100%   90.75kB/s    0:00:02 (xfer#17, to-check=1032/1063)
docs/api/index-files/index-13.html
      462569 100%  111.90kB/s    0:00:04 (xfer#18, to-check=1031/1063)
docs/api/index-files/index-14.html
      338993 100%  125.02kB/s    0:00:02 (xfer#19, to-check=1030/1063)
docs/api/index-files/index-15.html
      186915 100%   90.54kB/s    0:00:02 (xfer#20, to-check=1029/1063)
docs/api/index-files/index-16.html
      773522 100%  108.94kB/s    0:00:06 (xfer#21, to-check=1028/1063)
docs/api/index-files/index-17.html
       25326 100%   48.88kB/s    0:00:00 (xfer#22, to-check=1027/1063)
docs/api/index-files/index-18.html
      815983 100%  117.10kB/s    0:00:06 (xfer#23, to-check=1026/1063)
docs/api/index-files/index-19.html
     1853449 100%  120.97kB/s    0:00:14 (xfer#24, to-check=1025/1063)
docs/api/index-files/index-2.html
      361451 100%  103.79kB/s    0:00:03 (xfer#25, to-check=1024/1063)
docs/api/index-files/index-20.html
       98304  16%   95.33kB/s    0:00:05  
[sender] io timeout after 30 seconds -- exiting
rsync error: timeout in data send/receive (code 30) at io.c(140) [sender=3.0.8]

転送先のディレクトリを見ると以下のようになっている。

$ pwd
/home/username/to_dir/lowspeed/docs/api/index-files
$ ls | more
index-1.html
index-10.html
index-11.html
index-12.html
index-13.html
index-14.html
index-15.html
index-16.html
index-17.html
index-18.html

と、結果はオプションをつける前と変わらなかった。

また、実行中にテンポラリーディレクトリの中身を見たが何も存在していなかった

なお、テンポラリーではなく転送先ディレクトリの中身を監視したところ、転送先ディレクトリの中にテンポラリーファイルが作成されていた。

$ pwd
/home/username/to_dir/lowspeed/docs/api/index-files
$ ls -a | more
.
..
$ ls -a | more
.
..
.index-1.html.EMGxLc
$ ls -a | more
.
..
.index-10.html.rJKakp
index-1.html
$ ls -a | more
.
..
.index-12.html.x5E9O1
index-1.html
index-10.html
index-11.html

ということでこのオプションを使っても動きは何も変わらなかった

--temp-dir と --compare-dest の両方を使用した場合

rsync実行時のログは以下の通り。

$ rsync -avI --delete --progress -e ssh --bwlimit=128 --timeout=30 --temp-dir=/home/username/to_dir/tmp --compare-dest=/home/username/to_dir/tmp ./javadoc/ username@192.168.119.144:/home/username/to_dir/lowspeed
username@192.168.119.144's password: 
sending incremental file list
./
docs/
docs/index.html
       39679 100%    6.59MB/s    0:00:00 (xfer#1, to-check=1022/1025)
docs/api/
docs/api/allclasses-frame.html
      503784 100%  125.54kB/s    0:00:03 (xfer#2, to-check=1052/1063)
docs/api/allclasses-noframe.html
      427924 100%  103.41kB/s    0:00:04 (xfer#3, to-check=1051/1063)
docs/api/constant-values.html
     1267915 100%  116.77kB/s    0:00:10 (xfer#4, to-check=1050/1063)
docs/api/deprecated-list.html
      167204 100%  117.73kB/s    0:00:01 (xfer#5, to-check=1049/1063)
docs/api/help-doc.html
       10463 100%   26.89kB/s    0:00:00 (xfer#6, to-check=1048/1063)
docs/api/index.html
        1399 100%    3.60kB/s    0:00:00 (xfer#7, to-check=1047/1063)
docs/api/overview-frame.html
       28532 100%   44.09kB/s    0:00:00 (xfer#8, to-check=1046/1063)
docs/api/overview-summary.html
       60507 100%   58.45kB/s    0:00:01 (xfer#9, to-check=1045/1063)
docs/api/overview-tree.html
     1114950 100%  127.01kB/s    0:00:08 (xfer#10, to-check=1044/1063)
docs/api/package-list
        4115 100%    6.39kB/s    0:00:00 (xfer#11, to-check=1043/1063)
docs/api/serialized-form.html
     1499294 100%  121.04kB/s    0:00:12 (xfer#12, to-check=1042/1063)
docs/api/stylesheet.css
        1391 100%    1.37kB/s    0:00:00 (xfer#13, to-check=1041/1063)
docs/api/index-files/
docs/api/index-files/index-1.html
      749599 100%  108.19kB/s    0:00:06 (xfer#14, to-check=1035/1063)
docs/api/index-files/index-10.html
      258969 100%  118.01kB/s    0:00:02 (xfer#15, to-check=1034/1063)
docs/api/index-files/index-11.html
       84952 100%   94.17kB/s    0:00:00 (xfer#16, to-check=1033/1063)
docs/api/index-files/index-12.html
      269850 100%   90.59kB/s    0:00:02 (xfer#17, to-check=1032/1063)
docs/api/index-files/index-13.html
      462569 100%  112.04kB/s    0:00:04 (xfer#18, to-check=1031/1063)
docs/api/index-files/index-14.html
      338993 100%  125.11kB/s    0:00:02 (xfer#19, to-check=1030/1063)
docs/api/index-files/index-15.html
      186915 100%   90.63kB/s    0:00:02 (xfer#20, to-check=1029/1063)
docs/api/index-files/index-16.html
      773522 100%  109.05kB/s    0:00:06 (xfer#21, to-check=1028/1063)
docs/api/index-files/index-17.html
       25326 100%   49.17kB/s    0:00:00 (xfer#22, to-check=1027/1063)
docs/api/index-files/index-18.html
      815983 100%  117.48kB/s    0:00:06 (xfer#23, to-check=1026/1063)
docs/api/index-files/index-19.html
      720896  38%  126.92kB/s    0:00:08  
[sender] io timeout after 30 seconds -- exiting
rsync error: timeout in data send/receive (code 31) at io.c(140) [sender=3.0.8]

転送先のディレクトリを見ると以下のようになっている。

$ pwd
/home/username/to_dir/lowspeed/docs/api/index-files
$ ls  | more
index-1.html
index-10.html
index-11.html
index-12.html
index-13.html
index-14.html
index-15.html

と、結果はこれまでとと変わらなかった。

また、実行中のテンポラリーディレクトリの中身については --temp-dir 指定時と同様であった。

ということで --temp-dir と --comp-dest オプションを使っても動きは何も変わらなかった

ただ、なんとなく指定方法が間違っている、オプションが不足しているということがありそうな気がしてならない。



今回参考にさせていただいたサイト

xargsで全ファイルを touch する際に使い方を参考にさせていただいた
http://x68000.q-e-d.net/~68user/unix/pickup?xargs

CentOSでコマンドラインでネットワーク設定を変更したり起動停止したりする方法
http://d.hatena.ne.jp/bojovs/20081020/1224429418

rsync でのファイル操作についての情報
http://blog.livedoor.jp/sasata299/archives/51293802.html

--temp-dir と --comp-dest オプションについての説明
http://www.infoscience.co.jp/technical/rsync/rsync.html

2011年10月31日月曜日

rsyncでサブディレクトリから順に同期する

rsync を使用するとディレクトリ間の同期をとってくれるためとても便利である。
だが、同期の順番は

上位のディレクトリ → 下位のディレクトリ

であり、指定することができない。
だが、今回は逆の順番である

下位のディレクトリ → 上位のディレクトリ

で実施したい。

実現方法の案は

シェルスクリプトでディレクトリ構造を把握して、
サブディレクトリから上位ディレクトリの順に都度 rsync を実行する

だ。

ということでまずは指定したディレクトリ以下のディレクトリ構造を取得するコマンドだが、それは簡単に取得できる。

find {調査したいディレクトリ} -type d

あとは取得したディレクトリの一覧を tac コマンドで逆順にして rsync を実行するだけである。

つまりこんな感じ。

予めディレクトリを作成しておかないとエラーになるためスクリプトを修正しました。

#!/bin/sh

RSYNC_OPT='-av --delete'
RSYNC_OPT_FOR_DIR='-dr'
SRC_ROOT_DIR=$1
DST_ROOT_DIR=$2

#create directories

RSYNC_COMMAND="rsync ${RSYNC_OPT_FOR_DIR} ${SRC_ROOT_DIR} ${DST_ROOT_DIR}"
echo "COMMAND: ${RSYNC_COMMAND}"
${RSYNC_COMMAND}

#remove last "/"
case ${SRC_ROOT_DIR} in
*/)
  SRC_ROOT_DIR_CUT_LENGTH=`expr ${#SRC_ROOT_DIR} - 1`
  SRC_ROOT_DIR=`echo ${SRC_ROOT_DIR} | cut -c1-${SRC_ROOT_DIR_CUT_LENGTH}`
  ;;
esac
case ${DST_ROOT_DIR} in
*/)
  DST_ROOT_DIR_CUT_LENGTH=`expr ${#DST_ROOT_DIR} - 1`
  DST_ROOT_DIR=`echo ${DST_ROOT_DIR} | cut -c1-${DST_ROOT_DIR_CUT_LENGTH}`
  ;;
esac

#save current directory
INITIAL_DIR=`pwd`

#move src directory
cd ${SRC_ROOT_DIR}

# find all sub directories and sort reverse
targetdirectories=`find . -type d | tac`

#move initial directory
cd ${INITIAL_DIR}

# run rsync each sub directories reversely
for targetdirectory in ${targetdirectories}
do
  if [ ${#targetdirectory} -ge 2 ]
  then
    ACTUAL_DIRECTORY=`echo ${targetdirectory} | cut -c 2-${#targetdirectory}`
  else
    ACTUAL_DIRECTORY=''
  fi
  ACTUAL_SRC_DIRECTORY="${SRC_ROOT_DIR}${ACTUAL_DIRECTORY}/"
  ACTUAL_DST_DIRECTORY="${DST_ROOT_DIR}${ACTUAL_DIRECTORY}"
  RSYNC_COMMAND="rsync ${RSYNC_OPT} ${ACTUAL_SRC_DIRECTORY} ${ACTUAL_DST_DIRECTORY}"
  echo "COMMAND: ${RSYNC_COMMAND}"
  ${RSYNC_COMMAND}
done

備考


ただ、障害時のリカバリーはどうするか…?
→ rsync実行中にエラー(ネットワークがダウンなど)が発生すると途中までの処理結果が反映されてしまう によれば、通常実行時も途中で障害が発生した場合には途中まで反映された状態となるため問題なしか…?

参考にしたページなど

と、簡単に書いた様な書き方であるが、実は初めてシェルスクリプトを本格的に作ったこともありかなり苦戦した。
以下のページを参考にさせていただいた。先人に感謝。

rsync の使い方:
http://x68000.q-e-d.net/~68user/unix/pickup?rsync

find の出力結果を逆順にして処理する方法のヒント
http://www.mkamo.org/blog/20090613/377.html

コードのハイライト方法:
http://moririn-web.blogspot.com/2010/03/bloggersyntax-hilighter.html


2011年7月30日土曜日

ブログ開始

ふと思い立ってブログを始めてみた。

Twitterでちょっとだけ発信してみて抵抗がなくなったせいかもしれない。
日々思うことや勉強したことなどを記録していこうと思う。

それが誰かの笑顔につながることを祈って。