ペパボでは技術スタッフ正社員を募集しています
プログラマ と サーバーエンジニア の枠で正社員募集中です。
プログラマは、現在僕が出張で来ている福岡で、サーバエンジニアは東京での募集です。
社長から「頭がおかしい」と言われるような こんな僕が技術のトップなんぞやってる会社ですが、ドクターペッパーが好きなサーバエンジニアの方、ぜひ僕と一緒にリフレッシュエリアでドクターペッパー飲み飲み、社長の生態を観察しましょう。(リフレッシュエリアのソファに座ると、真正面にガラス越しの社長が観察できます。動物園気分が味わえますよ。)またはカレー(ハヤシ含む)を飲みながらでも OK です。
プログラマは福岡での募集ですが、たびたび出張で来ますので、ぜひ福岡オフィスの自販機にドクターペッパーを入れるよう、署名運動とかしてもらいたいと思います。そしてドクターペッパー飲みつつ、焼きカレーを食べましょう。
write はユーザプロセスを待たせない?の答え 2
前回のエントリ について、naoya さんから僕の期待以上の素晴らしい回答を頂きました。感謝感謝です。
詳細は上記エントリをじっくり読んで頂きたいのですが、僕の疑問への直接の答えとなるのが、まとめにある
- write はプロセスを待たせない
- ただし明示的に sync する場合は待たせる
ですね。「write はプロセスを待たせない」が正しい、というのは naoya さんの解説を読んで納得しました。プロセスが write するということと、それが実際にディスクに書き出されることを、区別しないで考えていたのが僕の敗因ですね。
また、こちらの環境で kjournald や qmail-queue プロセスが待たされていたのは、sync してるから、ということでした。kjournald は確かめていないのですが、qmail-queue のソースの中では確かに fsync() が実行されていました。考えてみれば当然ですよね。ジャーナルのメタデータやメールキューなど、大事なデータを write して sync しないなんてことはあり得ないでしょうし。
なので、id:hirose31さんからのコメント「fsync(2)のせい?」がずばり回答になっていました。ありがとうございます。今度カレーおごらせてください。
というわけで、「write はユーザプロセスを待たせない?」は「待たせない」が答え、ということで完結です。
あと、
ということですが free の結果では、プロセスに割り当てるためのメモリが足りてるかどうかはわかりますが、ページキャッシュ用にメモリが足りてるかどうかはわかりません。
実際そのサーバーが主な仕事で使っているデータは、合計でどのぐらいのサイズでしょう。おそらく、キューに溜まったメールその他 OS 上で必要なデータのサイズをあわせると 800MB を超えるんではないかなあと思います。外してたらごめん。
もおっしゃるとおりですね。プロセスに必要なメモリ容量と、キャッシュに必要なメモリ容量をちゃんと区別していませんでした。
となると、メモリを増やせば書き込み性能が向上する可能性があるのでは、と思ったのですが、今回のケースではおそらく向上することはなさそうです。というのも、iostat の値を見てると、avgqu-sz が 1 日平均でおよそ 5、短時間で見ると 10 とか 20 あるいはそれ以上ということがざらにあります。avgqu-sz はキューに入っていて待ちとなっている I/O リクエストの数なので、常に処理待ちとなっている I/O リクエストが存在する、ということなります。なので、ページキャッシュの容量の問題ではなく、ディスクの性能がボトルネックになっている、と言えそうです。(これも自分の勘違いなら恥ずかしいなー…メモリを簡単に増やせるなら、試してみたいんですけどね。)
naoya さん、詳細な解説ありがとうございました。大変勉強になりました。僕のために時間を割いて長文エントリ書いてくれた、と思うとちょっと萌えました。(べっ、別にあんたのために…、とか言われそうですが。)
write はユーザプロセスを待たせない? 6
naoyaグループ - naoyaの日記 - I/O, iowait にちょっと反応してみます。
write はページに dirty フラグを立てるだけなので決してユーザープロセスを待たせない
って、本当にそうなんでしょうか?(否定しているわけではなく、純粋な疑問です。)
最近、書き込みの多いメールサーバのディスク I/O 周りを調査していて、実際にどのプロセスの書き込みが多いのかを調べる方法がわからなかったため、I/O 待ちになっているプロセスをカウントして、そこから類推してみようと、まずは
while [ 1 ]
do
ps -eo comm,state|grep D|grep -v COMMAND >> ps.txt
sleep 1
done
な感じで STAT が D のプロセスを記録するスクリプトをしばらく走らせておいて、
sort ps.txt | uniq -c | sort -gr| more
でカウントしてみると、
9129 kjournald D
6576 qmail-queue D
2017 multilog D
1897 pdflush D
1373 qmail-send D
1094 procmail D
1032 syslogd D
な感じで、kjournald と qmail-queue が圧倒的に多い。これってどっちも書き込みがメインの仕事のはず。
man ps で見ても、
D Uninterruptible sleep (usually IO)
なので、書き込みでプロセスが待たされてるのだと思うのですが、何か間違ってますでしょうか?
ちなみに、書き込みが多いというのはどれぐらいかというと、iostat での r/s と w/s の比率が 1:7 ぐらいです。
また、free の結果はこんな感じなので、メモリに余裕はあるみたいです。
# free
total used free shared buffers cached
Mem: 2043756 2011160 32596 0 351016 847776
-/+ buffers/cache: 812368 1231388
Swap: 1052248
ここ数日 I/O まわりを追いかけていたので、naoya さんのエントリはタイムリーでとてもためになります。ついでに色々教えてもらおうという甘い考えで、疑問に感じたことを書いてみました。
追記
もしかして「write はユーザプロセスを待たせない」というのは、非同期 I/O の話?であれば納得はいく。
FizzBuzz アセンブラ版 for x86/Linux
竹迫さん、Yappo さん に触発されて、FizzBuzz アセンブラ版 for x86/Linux をつくってみた。
20年ほど前に Z80 でアセンブラをちょっとかじった程度の知識しかないので、ベストには程遠いコードだと思います。だれかもっといいコードを教えてください。
最初竹迫さんのコードと同じように書けるかな、と思ったのですが、Windows とちがって、画面に表示するだけで EAX, EBX, ECX, EDX レジスタ使うので、竹迫さんのように BX レジスタを見張り役に、CX レジスタをカウンタに、ってことができませんでした。
また、とりあえず書いただけで疲れたので、コードゴルフにチャレンジする気力はありません。
global _start
_start:
mov si, 0
mawasu:
call space
inc si
mov ax, si
mov di, 3
xor edx, edx
div di
cmp dx, 0
je pfizz
mov ax, si
mov di, 5
xor edx, edx
div di
cmp dx, 0
je pbuzz
call num
cmp si, 100
jb mawasu
pfizz:
call fizz
mov ax, si
mov di, 5
xor edx, edx
div di
cmp dx, 0
je pbuzz
jmp mawasu
pbuzz:
call buzz
jmp mawasu
num:
mov ax, si
mov di, 100
cmp ax, di
jnge num2
jmp end
num2:
mov di, 10
cmp ax, di
jnge num3
xor edx, edx
div di
add ax, 48
call print
num3:
mov ax, si
mov di, 10
xor edx, edx
div di
mov al, dl
add al, 48
call print
ret
fizz:
mov al, 0x46
call print
mov al, 0x69
call print
mov al, 0x7a
call print
call print
ret
buzz:
mov al, 0x42
call print
mov al, 0x75
call print
mov al, 0x7a
call print
call print
ret
space:
mov al, 0x20
call print
ret
print:
push eax
mov eax, 4
mov ebx, 1
mov ecx, esp
mov edx, 1
int 0x80
pop eax
ret
end:
mov al, 0x0a
call print
mov al, 1
mov bl, 0
int 0x80
nasm を使ってこんな感じで動かすことができます。
$ nasm -f elf fizzbuzz.asm
$ ld -s -o fizzbuzz fizzbuzz.o
$ ./fizzbuzz

書いていてアセンブラの TMTOWTDI っぷりは Perl の比じゃないと思った。
Re: DBICとDBIx::Class::Schema::Loader 僕のいろいろな勘違い 2
ブログが続かないわけ | DBICとDBIx::Class::Schema::Loader 僕のいろいろな勘違い にて、
とはいえ、僕の稼働中のアプリはすでに手動型のSchema で動いている。スキーマを作り直したら、リレーションの設定を全てしなおさなければならないので、それは現実的じゃない。inflate, deflate の指定は、やっぱりすべてのSchema にかかなきゃだめそうだ。
とあったので、これに関して少し楽ができる方法をコメントしようと思ったけれど、コメント欄ではうまく伝えられる自信がないので、こちらで書いてみることにしました。
load_components で 読み込む方法
DBIx::Class::Schema::Loader ではなく DBIx::Class::Schema を継承したスキーマの場合には、各スキーマファイルに以下の様に書いてあげれば OK です。(既にご存知かもしれませんが。)
package My::Schema::Table;
__PACKAGE__->load_components(
"InflateColumn::DateTime",
"PK::Auto",
"Core",
);
すべてのスキーマファイルに書かなきゃいけないことには変わりありませんが、各カラムに inflate/deflate を設定するよりははるかに楽だと思います。
make_schema_at を使う方法
make_schema_at でスキーマファイルを生成しているのであれば、こんな感じでリレーションだけ定義した ./tmp/lib/My/Schema/Table.pm をまず用意します。
package My::Schema::Table;
__PACKAGE__->belongs_to(
realation => 'My::Schema::OtherTable
{ 'foreign.id' => 'self.other_table_id' },
);
1;
でもって、こんなスクリプトを実行します。
#!/usr/bin/perl
use strict;
use warnings;
use lib qw( ./tmp/lib );
use Carp;
use DBIx::Class::Schema::Loader qw( make_schema_at dump_to_dir:lib dump_overwrite );
make_schema_at(
'My::Schema::Table',
{
components => [qw/ ResultSetManager UTF8Columns InflateColumn::DateTime /],
dump_overwrite => 1,
},
['dbi:mysql:dbname' ,'user', 'password'],
);
そうすると、lib/My/Schema/Table.pm に以下の内容を吐き出してくれます。
package My::Schema::Table;
# Created by DBIx::Class::Schema::Loader v0.03009 @ 2007-04-26 18:21:23
use strict;
use warnings;
use base 'DBIx::Class';
__PACKAGE__->load_components(
"ResultSetManager",
"UTF8Columns",
"InflateColumn::DateTime",
"PK::Auto",
"Core",
);
__PACKAGE__->table("table");
__PACKAGE__->add_columns(
"id",
{ data_type => "INT", default_value => undef, is_nullable => 0, size => 11 },
"other_table_id",
{ data_type => "INT", default_value => 0, is_nullable => 0, size => 11 },
"date",
{ data_type => "DATE", default_value => undef, is_nullable => 1, size => 10 },
__PACKAGE__->set_primary_key("id");
# These lines loaded from user-supplied external file:
package My::Schema::Table;
__PACKAGE__->belongs_to(
employees => 'My::Schema::Table',
{ 'foreign.id' => 'self.other_table_id' },
);
1;
# End of lines loaded from user-supplied external file
この方法を使うと、スキーマを作り直しても、リレーションの再設定をしなくて済みます。
手動でやるのが楽なのか、上の方法のいずれかを使うのが楽なのかは、状況にもよると思いますが、こんな方法もありますよ、ということで、ご参考になれば幸いです。
blosxom から typo へ
色々あって、typo へ移行してみた。
SVN::TracWiki #2
SVN::TracWiki (svn repos) をアップデート。以下の機能を実現するパッチを、Assurer の開発にも参加してくれている franck が送ってくれました。
- Plugin::Extract::Pod で Perl プログラムのコミット時に、pod を自動で抽出して Wiki にアップ。
- xmlrpc プラグインがなくても Wiki ポストができる。
Trac はソースコード自体の検索ができないので、当然 pod の内容も検索することができないのですが、このプラグインにより、pod の検索ができるようになります。
また、検索だけではなく、こんな感じ で pod を Trac の Wiki フォーマットに変換してキレイに表示してくれます。
試しに手元にあった XML::Atom をつっこんでみると、こんな感じ になります。
あと、File::Extract 内部で利用している File::MMagic::XS が謎の Segmentation Fault で落ちるので、File::MMagic を使うように強引に書き換えたり。
SVN::TracWiki #1
SVN::TracWiki (svn repos) について、ブクマコメントで miyagawa さんから「File::Extract?」というアドバイス?を頂きましたので、テキスト抽出部分を File::Extract ベースに書き換えてみました。
おかげでプラグインまわりの処理が少しすっきりした上に、File::Extract で対応しているファイルフォーマットであれば、SVN::TracWiki 用のプラグインを書かなくてもテキスト抽出ができるようになりました。(Excel で試しましたが、ばっちり動作しました。)
SVN::TracWiki #0
SVN::TracWiki というツールをつくってみました。svn repos はこちら。
何をするものかというと、Subversion の post-commit スクリプトとして動作して、コミットされたファイルからテキストを抽出、そのファイルの実体へのリンクを付加して、Trac の Wiki へ自動ポストするというもの。
具体的な例としては、PowerPoint ファイルをコミットしたら、そのファイルからテキストのみを抽出して Wiki へ自動ポスト。こんな感じで。
これで何がうれしいかというと、Subversion で管理している PowerPoint ファイルを、Trac 上で検索ができるようになります。こんな感じですね。元ファイルへのリンクもあるので、検索して元ファイルを開いて読む、ってことが簡単にできます。
例によって YAMLで設定 and プラガブル。ファイルからテキストを抽出する部分がプラグインになっていて、簡単に拡張できるようにしています。
現在は PowerPoint 用フィルタプラグインしかないのですが、以下の様なコードになっていて、フィルタ対象ファイルの MIME タイプを register() で指定、テキスト抽出ルーチンを filter() に記述、という感じで書きます。
package SVN::TracWiki::Plugin::Filter::PowerPoint;
use strict;
use warnings;
use base qw( SVN::TracWiki::Plugin::Filter );
use Encode;
sub register {
my $self = shift;
$self->register_mime_types( qw! application/vnd.ms-powerpoint !);
}
sub filter {
my ( $self, $file ) = @_;
my $html = `/usr/local/bin/ppthtml $file`;
my $text = $self->strip_html($html);
$text = Encode::decode('utf8', $text);
$text = Encode::encode('utf8', $text);
return $text;
}
1;
とりあえず動くようになっただけで、いけてない部分盛りだくさんですが、こんなのつくってみました、ってことで。
html-tt-mode を sgml-mode のマイナーモードで動くようにしてみた
Clouder::Blogger: html-tt - emacsのTemplate Toolkit用のmode を html-helper-mode ではなく、sgml-mode のマイナーモードで動くようにしたパッチ。(置換しただけ。)
別に sgml-mode に思い入れはなく、Meadow でデフォルトで動いてるからそのまま使ってるだけなので、html-helper-mode にしてもいいんだけど、どうも Meadow でうまく色づけされないので、その原因追求するよりも、sgml-mode で動くようにした方がはやかったので。
本当はどっちでも動くようにするのがいいんだろうけど、それはまた今度。
=== html-tt.el
==================================================================
--- html-tt.el (revision 193)
+++ html-tt.el (local)
@@ -66,7 +66,7 @@
;; Code:
(provide 'html-tt)
-(require 'html-helper-mode)
+(require 'sgml-mode)
(require 'tempo)
(require 'font-lock)
@@ -217,38 +217,38 @@
(defun html-tt-load-hook ()
(interactive)
;; define key bind
- ;(define-key html-helper-mode-map "\C-cs"
+ ;(define-key sgml-mode-map "\C-cs"
; 'html-tt-insert-sequence)
- (define-key html-helper-mode-map "\C-cs"
+ (define-key sgml-mode-map "\C-cs"
'tempo-template-html-tt-insert-sequence)
- (define-key html-helper-mode-map "\C-cd"
+ (define-key sgml-mode-map "\C-cd"
'html-tt-insert-directive)
- (define-key html-helper-mode-map "\C-cn"
+ (define-key sgml-mode-map "\C-cn"
'tempo-template-html-tt-insert-directive)
- (define-key html-helper-mode-map "\C-ci"
+ (define-key sgml-mode-map "\C-ci"
'tempo-template-html-tt-insert-if)
- (define-key html-helper-mode-map "\C-cl"
+ (define-key sgml-mode-map "\C-cl"
'tempo-template-html-tt-insert-elsif)
- (define-key html-helper-mode-map "\C-ce"
+ (define-key sgml-mode-map "\C-ce"
'tempo-template-html-tt-insert-else)
- (define-key html-helper-mode-map "\C-cf"
+ (define-key sgml-mode-map "\C-cf"
'tempo-template-html-tt-insert-foreach)
- (define-key html-helper-mode-map "\C-cw"
+ (define-key sgml-mode-map "\C-cw"
'tempo-template-html-tt-insert-while)
- (define-key html-helper-mode-map "\C-cm"
+ (define-key sgml-mode-map "\C-cm"
'tempo-template-html-tt-insert-switch)
- (define-key html-helper-mode-map "\C-cn"
+ (define-key sgml-mode-map "\C-cn"
'tempo-template-html-tt-insert-include)
;; add hilit-set-mode-pattern, if use hilit19.
(if (featurep 'hilit19)
- (hilit-add-pattern "\\[%" "%\\]" 'midnightblue 'html-helper-mode)
+ (hilit-add-pattern "\\[%" "%\\]" 'midnightblue 'sgml-mode)
)
;; set font-lock
(make-local-variable 'font-lock-defaults)
(setq html-tt-font-lock-keywords
- (append html-helper-font-lock-keywords html-tt-font-lock-keywords))
+ (append sgml-font-lock-keywords html-tt-font-lock-keywords))
(setq font-lock-defaults '(html-tt-font-lock-keywords t t))
)
.emacs での設定はこんな感じで。
(require 'html-tt) (add-hook 'sgml-mode-hook 'html-tt-load-hook)