[pgsql-jp: 35382] Re: 太る pgpool

Tatsuo Ishii t-ishii @ sra.co.jp
2005年 5月 14日 (土) 09:17:56 JST


石井です.

pgsql-jpが復旧したのでそちらにもCc:します.

> 鹿持@メダカカレッジです。
> 
> 直メールでごめんなさい。
> pgsql-jp が止まっているようなので。。。
> 
> 引用を前後させています。
> 
> At Sat, 07 May 2005 20:57:34 +0900 (JST),
> Tatsuo Ishii wrote:
> 
> > > で、これを見ていて気付いたのが1点。
> > > pid 3626 のものが親だと思うんですが、こいつがやたらとメモリを喰ってます。
> > > あれれ、と思ってソースを見ると、pool_error.c の 100行目過ぎにある
> > > pool_log() の中で asprintf() を使ったあと、 free() をしてないですね。
> > > health_check を1秒ごとにする設定にしていたので、"starting health checking" が
> > > 毎秒ごとに出るんですが、これが毎度リークして太ってるんじゃないかと思います。
> > ご指摘の通りでした.
> > 
> > > # あと main.c の l.247 で
> > > #   pids = malloc(pool_config.num_init_children * sizeof(pool_config.num_init_children));
> > > # とあるのは
> > > #   pids = malloc(pool_config.num_init_children * sizeof(pid_t));
> > > # が正しい?
> > はい,こちらもそうですね(そのすぐ後ろにも同じような間違いがあります).
> 
> cvs から新しい mail.c と pool_error.c をいただいてきて、leak の修正を
> 確認しました。
> ありがとうございました。
> 
> で、ゾンビの件ですが、
> 
> > > pgpool 2.5.2 を replication_mode = true で運用し、2台の PostgreSQL に対して
> > > クエリを投げるようにしているサーバで、あるときアプリから接続ができなくなる
> > > ということがありました。
> > > pgpool への接続がだめで、PostgreSQL への直接接続は大丈夫でした。
> > > killall -9 で pgpool を殺して、再起動したところ無事復旧。
> > > 
> > > さて、この障害が起きていたときの ps auxw | grep pgpool を見ると
> > > 以下の感じでした。
> > > (ちょっと横に長くてごめんなさい)
> > [snip]
> > > なんかやたらとゾンビがいます。(^^;
> > > num_init_children は 150 にしているのですが、数えたら 101 がゾンビに
> > > なってました。なんでじゃ。。。
> > これはちょっとわかりませんが...
> 
> ふと気付いて syslog を見ると、毎秒の health check のログが途中で止まってました。
> 
>  - あるところで親 pgpool が刺さる
>  - 子 pgpool が child_life_time 以上アイドル状態が続き、自害する
>  - しかし wait() してくれる親がいないため、ゾンビになる
> 
> というシナリオかしら、と。
> 
> しかしどこで刺さってるかソースからだけで考えるのはしんどいので、
> 再現しないかなぁと待っていたのですが、今朝再現してくれたので、
> 親 pgpool を gdb で attach。backtrace をとりました。
> 
> | (gdb) where
> | #0  0xb75ebc32 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
> | #1  0xb7540c76 in __lll_mutex_lock_wait () from /lib/tls/libc.so.6
> | #2  0xb758d660 in run_fp () from /lib/tls/libc.so.6
> | #3  0xb758cd98 in __DTOR_END__ () from /lib/tls/libc.so.6
> | #4  0xb758d660 in run_fp () from /lib/tls/libc.so.6
> | #5  0xb74cd194 in _L_mutex_lock_10108 () from /lib/tls/libc.so.6
> 
> 。。。
> なんか終わろうとしてそうですが、これでは何がなんだかわからない。
> スタックからそれっぽい番地を拾っていくと fork_a_child を見つけました。
> 
> | (gdb) x/10i fork_a_child
> | 0x8049ea4 <fork_a_child>:       push   %ebp
> | 0x8049ea5 <fork_a_child+1>:     mov    %esp,%ebp
> | 0x8049ea7 <fork_a_child+3>:     push   %ebx
> | 0x8049ea8 <fork_a_child+4>:     push   %eax
> | 0x8049ea9 <fork_a_child+5>:     call   0x8049008
> | 0x8049eae <fork_a_child+10>:    test   %eax,%eax
> | 0x8049eb0 <fork_a_child+12>:    mov    %eax,%ebx
> | 0x8049eb2 <fork_a_child+14>:    je     0x8049ef0 <fork_a_child+76>
> | 0x8049eb4 <fork_a_child+16>:    cmp    $0xffffffff,%eax
> | 0x8049eb7 <fork_a_child+19>:    je     0x8049ec0 <fork_a_child+28>
> |
> | (gdb) x/10i 0x8049008
> | 0x8049008:      jmp    *0x8059468
> | 0x804900e:      push   $0x60
> | 0x8049013:      jmp    0x8048f38
> | 0x8049018:      jmp    *0x805946c
> | 0x804901e:      push   $0x68
> | 0x8049023:      jmp    0x8048f38
> | 0x8049028:      jmp    *0x8059470
> | 0x804902e:      push   $0x70
> | 0x8049033:      jmp    0x8048f38
> | 0x8049038:      jmp    *0x8059474
> |
> | (gdb) x/1x 0x8059468
> | 0x8059468 <_GLOBAL_OFFSET_TABLE_+60>:   0xb7501060
> |
> | (gdb) x/10i 0xb7501060
> | 0xb7501060 <fork>:      push   %ebp
> | 0xb7501061 <fork+1>:    mov    %esp,%ebp
> | 0xb7501063 <fork+3>:    push   %edi
> | 0xb7501064 <fork+4>:    push   %esi
> | 0xb7501065 <fork+5>:    push   %ebx
> | 0xb7501066 <fork+6>:    call   0xb746e57d <__i686.get_pc_thunk.bx>
> | 0xb750106b <fork+11>:   add    $0x8bd2d,%ebx
> | 0xb7501071 <fork+17>:   sub    $0x2c,%esp
> | 0xb7501074 <fork+20>:   movl   $0x0,0xffffffdc(%ebp)
> | 0xb750107b <fork+27>:   mov    0x1a4(%ebx),%eax
> 
> main -> reap_handler -> fork_a_child -> fork -> __i686.get_pc_thunk という
> 経路で来て、そこで固まっているようです。
> 
> fork() を signal handler の中から呼んでますが、これがまずいということは
> ないでしょうか。

「signal handlerの中ではなるべく余計なことをするな」ということは教訓と
して良く言われるのですが,fork()がタブーなのかはよくわかりません.もう
ちょっと調べてみようと思いますが...

> そして、子 pgpool の看取りは waitpid(WNOHANG) でやっていますし、
> 子の再起動のための fork_a_child(reap_handler)を signal handler から
> 呼ぶのではなく、main() のメインループの中から定期的にキックするようにする、
> というアイディアもあると思うんですがいかがでしょう?

とりあえずこちらの方向で検討します.heal checkを動かしているときに,起
動するタイミングを探すのがちょっと微妙ではありますが.
--
Tatsuo Ishii

> # 「signal handler から」の fork() が犯人かどうか確定ではないですが。
> 
> 
> ● from: KAJI Wataru <waasuke @ medaka-college.com>
> ● 鹿持 渉 @ メダカカレッジ
>http://www.medaka-college.com/
> 



pgsql-jp メーリングリストの案内