Sunday, February 24, 2013

WindowsでもTZ環境変数を設定しよう


Windowsにおける言語環境の設定について、見落としていたことがあったのでメモ。

まず、私は自宅では、Windows7(64bit)上で Meadow を入れて mew でメールの読み書きをしている。

MUAの表示はこんな↓感じ。

| X-Mailer: Mew version 6.1 on Emacs 22.1 / Mule 5.0 (SAKAKI)

さて、本日のお題は mew で送信されるメールの Date: フィールドの TimeZone 表示である。
実は、つい最近まで私が自宅から送信するメールにはこんな Date: フィールドが付いていたのだった。

| Date: Fri, 22 Feb 2013 23:28:12 +0900 (東京 (標準時))

最後の Time Zone名が、JST になってほしいところが「(東京 (標準時))」と日本語表示になってしまっている。 :(
一応、生ヘッダ的には

| Date: Fri, 22 Feb 2013 23:28:12 +0900
|  =?iso-2022-jp?B?KBskQkVsNX4bKEIgKBskQkk4PWA7fhsoQikp?=

ということで、encoding 処理はされてはいるのだが、これはうれしくない。

調べてみたところ、実はこの話は10年以上前に mule の MLで議論されていたことがわかった。

http://www.m17n.org/mlarchive/mule-ja/200102/msg00071.html

環境変数で TZ を設定しない限り、Meadow や mew の設定で回避する方法は今でもなさそうだ。ただ、本人としては、各種ツールの都合があるので、そもそも TZ を設定しているつもりで抜けていたという手落ちが今になって発覚したというオチなのだった...orz

「コンピュータ」のプロパティ→「システムの詳細設定」→「環境変数」から、

  TZ=JST-9

を設定し、送信したメールの Time Zone名表示が

| Date: Sat, 23 Feb 2013 23:54:43 +0900 (JST)


と期待通りに変わったことを確認して作業完了。

しかし、さかのぼってみると、結構前からこの望ましくない状態が続いていたことが発覚し、へこんでいる今日このごろ...orz

Monday, February 18, 2013

不審な外部向けアクセスが...

socket APIのトレースを見ていて妙なことに気が付いたことがあったのでメモ。

connect の第2引数にポインタでわたってくるstruct sockaddr_in の中身が

  sin_family = 1
  port       = 12150
  sin_addr   = 97.114.47.114

なんていうコネクション確立要求が出ているらしい。

完全孤立した外部につながらない環境だったし、8.8.8.8 みたいなコネクティビティチェックに無造作に使われるアドレスでもないので、「これはっ!?」と思って色めきだったのだった。

まずは「このIPって何?」ということで調べてみると...

| nslookup 97.114.47.114
| Server:         8.8.8.8
| Address:        8.8.8.8#53
| Non-authoritative answer:
| 114.47.114.97.in-addr.arpa      name = 97-114-47-114.roch.qwest.net.

うーん、なんだかよくわからない。

じゃあ…ということで、IPとportでググってみると、
こんなの↓とか、
http://comments.gmane.org/gmane.os.freebsd.devel.sparc/479
こんなの↓とか、
http://sourceforge.jp/projects/ultramonkey-l7/lists/archive/develop/20081208/000221.html
出てきた。
しかも2つめは日本語の L7-UltraMonkey のコミュニティの記事な上に、ずばありこんな記述が...

|  ○ 原因
|  
|      UNIX ドメインソケットなので sun_path を表示すべきところを,sin_addr と
|    sin_port を表示していた.

そう。
上記のログは、自前で socket()まわりの動きを確認していたつもりが、sin_family の値をきちんとチェックしていなかったために、こんなことになっていたのだった。

実際、冒頭で書いた sin_family = 1 は AF_UNIX でずばり。見落としてました...orz

なお、97.114.47.114:12150 というのは、どうも resolver 系で典型的に使う UNIXドメインソケットのパス名(の一部)に対応しているらしい。

大騒ぎする前に、ちゃんと追いかけておいて良かった...orz

Tuesday, February 12, 2013

UbuntuのsleepenhとRedHatのusleepの違い

いろいろ検証っぽいことをしていると、1秒単位でしか指定できない sleep コマンドでは足りなくて、usleep を使うことがよくある。

ただし、これは RedHat 系固有のコマンドなので、Ubuntu (や Debian)でも使いたいと思って調べて初めて知ったことがあったのでメモ。

話は簡単で、sleepenh コマンドを使う。

私はこの記事で見つけた。

現行の 12.10 (Quantal) に対応するパッケージはこれ


で、man page はこんな感じ。


文字通り拡張版sleepということのようだが、コマンド名を変えているだけあって仕様に違いがある。

SLEEPENH(1)                                                        SLEEPENH(1)

NAME
       sleepenh - an enhanced sleep program.

SYNOPSIS
       sleepenh [initial-time] sleep-time

DESCRIPTION
       sleepenh  is a program that can be used when there is a need to execute
       some functions periodically in a shell script. It was not  designed  to
       be  accurate  for  a  single sleep, but to be accurate in a sequence of
       consecutive sleeps.
       After a successful execution, it returns to  stdout  the  timestamp  it
       finished running, that can be used as initial-time to a successive exe-
       cution of sleepenh.

OPTIONS
       There are no command line options. Run it without any option to  get  a
       brief help and version.

ARGUMENTS
       sleep-time is a real number in seconds, with microseconds resolution (1
       minute, 20 seconds and 123456 microseconds would be 80.123456).
       initial-time is a real number in seconds, with microseconds resolution.
       This number is system dependent. In GNU/Linux systems, it is the number
       of seconds since midnight 1970-01-01 GMT. Do not  try  to  get  a  good
       value  of  initial-time. Use the value supplied by a previous execution
       of sleepenh.
       If you don't specify initial-time, it is assumed the current-time.

usleep の場合は単純にマイクロ秒単位の整数値で指定できる版のsleepなのだが、sleepenh の場合は、引数(上記の 'sleep-time' )に秒単位で指定する。じゃあ、500ms sleep したい場合はどうするか?と言えば、赤太字で強調したように、引数は整数ではなく「実数」ということになっているので、0.5 と指定すればよい。精度は、仕様上はマイクロ秒まで。

なお、精度が「仕様上」マイクロ秒まで…と書いたのには理由があって、ライブラリ/kernelの内部的な処理には適当な丸めが入るので、マイクロ秒単位の精度で指定したからといってもかならずしもそうなるわけではない。このあたり、書きはじめるとけっこうな量になるので、さてやりますか…と、思ってまずは関連文献の調査ということで、ぐぐったら、id:naoya さんのすばらしい記事が出てきた... :o

「Linux のスリープ処理、タイマ処理の詳細を見る」
  http://d.hatena.ne.jp/naoya/20080122/1200960926

さすが…

(この精度のくだりのあたり、@_hito_ さんからご指摘をいただいて追記しました。@_hito_ さん、どうもありがとうございます!)

なお、man page を長々と引用したのにも理由があって、実数を2つ指定していろいろ凝ったことができる...、特に途中で(可変時間の)処理をはさみながらも、決まった間隔でループをまわしたい場合等に便利な仕様になっているので、良く読んで使うべしということで。(指定する時間の単位の違いより、実はこっちのほうが重要な気がする....)

Saturday, February 9, 2013

TCPの再送タイムアウトを制御したい


TCPの再送タイムアウトで最近まで知らなかったことがあったのでメモ。

たとえば、APサーバとDBサーバがあるとする。
AP-DB間のDBCPで使うTCPコネクションは、DBサーバがノードダウンしたのであればさっさと再送リトライをあきらめて切れてほしいと思うのが普通だろう。
このTCPの再送リトライ処理は、何もいじらないと15分以上続くので、
できればTCPコネクション単位で細かく調整させてほしいと思うのは人情だと思う。

Linux の場合、昔はこの調整をしようと思うと、sysctl を使って /proc/sys/net/ipv4/tcp_retries2 の値を書き換えるしかなかった。
これは、リトライ回数でしか指定できず、そのリトライ間隔が回数ごとに変わっていく(長くなる)のでわかりにくい上に、そのOS上の全TCPコネクションで有効になってしまうという問題があった。つまり、このオプションは「TCPコネクション切れやすさ」を調整しているとも言えるので、例えばインターネット向けと内部LAN向けのTCPコネクションは別の切れやすさの設定にしたい…といった場合に困ったことになるのだった。

なお、tcp_retries2 の指定値によるタイムアウト時間の変化については、日本語でも詳しい解説がたくさんある。例えば以下の記事とか。(あれ?良く見てみたら、@int128 さんの記事じゃん... :o)

http://d.hatena.ne.jp/int128/20100514/1273865819


さて、Stackoverflow で見つけた以下の記事に紹介があるのだが、

http://stackoverflow.com/questions/5907527/application-control-of-tcp-retransmission-on-linux

linux-2.6.37 から、setsockopt(2)で指定可能なオプションに TCP_USER_TIMEOUT というものが追加されている。

upstream の commit log はこれ。

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=dca43c75e7e545694a9dd6288553f55c53e2a3a3


これを使うと、TCPコネクション単位で、しかもリトライ回数ではなくミリ秒単位で、TCPの再送処理をあきらめて ETIMEDOUT でエラーにするまでの時間を調整できる。
socket API としては、Linux 固有機能になるので移植性はなくなるのだが、現実問題としてはとても便利な機能である。


ここからはやや余談。

setsockopt() で IP や TCP のパラメータをいじる時には、

       #include <sys/types.h>
       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <netinet/tcp.h>

といったシステムヘッダを使うことになるのだが、少なくとも Ubuntu 12.10 (kernel は3.5系)では、本来あるべき /usr/include/netinet/tcp.h で TCP_USER_TIMEOUT は未定義なので、コンパイルエラーになってしまう。

ではどこで定義されているのか?と言えば、 以下のように /usr/include/linux/tcp.h に定義が存在する。

 /usr/include/linux/tcp.h より

102 #define TCP_QUICKACK            12      /* Block/reenable quick acks */
103 #define TCP_CONGESTION          13      /* Congestion control algorithm */
104 #define TCP_MD5SIG              14      /* TCP MD5 Signature (RFC2385) */
105 #define TCP_COOKIE_TRANSACTIONS 15      /* TCP Cookie Transactions */
106 #define TCP_THIN_LINEAR_TIMEOUTS 16     /* Use linear timeouts for thin streams*/
107 #define TCP_THIN_DUPACK         17      /* Fast retrans. after 1 dupack */
108 #define TCP_USER_TIMEOUT        18      /* How long for loss retry before timeout */
109 #define TCP_REPAIR              19      /* TCP sock is under repair right now */
110 #define TCP_REPAIR_QUEUE        20
111 #define TCP_QUEUE_SEQ           21
112 #define TCP_REPAIR_OPTIONS      22

ただし、ここで問題があって、/usr/include/linux/tcp.h は、device driver 等の kernel module が使うものであって、基本的にユーザプログラムが使ってはいけないことになっている。

ではどうすればよいのか?

実は、TCP_USER_TIMEOUT はマクロでしかないので、18 と直書きする…のがあんまりだと思えば、以下のようにしておくと、将来 netinet/tcp.h を include するだけで TCP_USER_TIMEOUT が使えるようになった時にもそのままビルドできるし、可読性も確保できる(はず)

(動作確認用に作った toy sample より)
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <sys/socket.h>
  6 #include <netinet/in.h>
  7 #include <netinet/tcp.h>
★ここ↑で代わりに <linux/tcp.h> を include してはいけない。
  8 #include <arpa/inet.h>
  9 #include <errno.h>
 10
 11 int main(int argc, char *argv[])
 12 {
 13         int sockfd, ret, option;
 14         struct sockaddr_in saddr;
   :
 32 #ifndef TCP_USER_TIMEOUT
 33 #define TCP_USER_TIMEOUT 18
 34 #endif
 35         option = 5 * 1000;  /* 5 seconds */
 36         ret = setsockopt(sockfd, IPPROTO_TCP, TCP_USER_TIMEOUT,
 37                          (void *)&option, sizeof(option));
 38         printf("setsockopt TCP_USER_TIMEOUT returns %d/%d\n", ret, errno);
 39         if (ret < 0) exit(0);


参考までに Ubuntu 12.10 に付属している /usr/include/netinet/tcp.h は

itoumsn@gateway:~/src/linux-3.3.1-tag$ dpkg -S /usr/include/netinet/tcp.h
libc6-dev:amd64: /usr/include/netinet/tcp.h
itoumsn@gateway:~/src/linux-3.3.1-tag$ dpkg -l | grep libc6-dev
ii  libc6-dev:amd64   2.15-0ubuntu20  amd64  Embedded GNU C Library: Development Libraries and Header Files

という感じなのだが、いつごろ取り込んでもらえますかねぇ...

あともうひとつ気になるのは、RHEL6 の kernel にバックポートされてくれると嬉しいのだがなぁ...
まだ確認していないのだが、どうかなぁ...

TCP_USER_TIMEOUT 未サポートの kernel の場合は、setsockopt(2)が ENOPROTOOPT (92) でエラーになるようだ。
(fade out)

Sunday, February 3, 2013

MySQL版の pgbench ってないの?


先日、ふとしたことから MySQL に軽く負荷をかけてみる必要が発生した。
できれば単発のSQLを投げるだけではなくて、複数threadで、しかもきちんと transaction になっているようなベンチマークが望ましい…というのが要件。
なお、当然 sysbench は試したのだが、あれは正常に動いていても一定確率で deadlock が発生する性質のもののようなので、今回の目的のためには使えなかった。

それならTPC系のOSSなベンチで何か手ごろなのがないかなぁ…と考えて、PostgresSQLならお手軽に pgbench (TPC-B相当)があるのだが、MySQLではどうなの?…というのが本日のお題。

さていきなり余談だが、マルチプラットフォームで同じベンチが動けばいいなら、JDBCBench という Java で書かれた TPC-B もあることはある


のだが、事情でむかーし使ってみた時には、この移植って少し問題があって修正が必要だったような記憶があったのと、今回は環境の都合でJVMなしでお手軽に動かしたかったのでググってみたところ、日本MySQLユーザ会の有志の方が移植したものを発見。
(当然みんな同じことを考えるんだねぇ…ということで :)


PostgreSQL 付属の pgbench を移植したもののようだ。

ではさっそく…ということで、MySQL 5.5 で動かしてみたところ、DBの初期化のところでエラーになってしまった...orz

ざっと調べたところ、これはそもそも2004年の仕事で、対応している MySQL のバージョンが古く、create table するところでInnodbとかのstorage engineを指定するDDLの syntax が古いだけだということが判明。

mysqlbench.c に以下のようなところがあるので、403行目の TYPE= を ENGINE= に修正するだけで動くようになる。

    372 /*===================================*/
    373 /* create tables and setup data */
    374 static void init(MYSQL *mysql)
    375 {
    376   MYSQL    *con;
    377   int        res;
      : 
    399   for (i = 0; i < (sizeof(DDLs) / sizeof(char *)); i++)
    400   {
    401     if (strncmp(DDLs[i], "CREATE", 6) == 0)
    402     {
    403       sprintf(sql, "%s TYPE=%s", DDLs[i], engine);
    404     }
    405     else



とかあるようだし、もっと探せばどこかで最新版がメンテされているのかなぁ…と、思いつつ、目的は果たせてしまったので、ここまでとした。

MySQLのDDL syntax がいつ変わったのかわからないのだけど、サーバのバージョンをみて、TYPE= と ENGINE= を切り替えるような処理をするのがよさげですね。