[pgpool-general-jp: 131] pgpool-II パラレルモード使用時における画像登録エラー

asano asano @ engm.com
2007年 5月 22日 (火) 17:01:49 JST


お世話になっております。エニグマ浅野です。


pgpool-II パラレルモード使用下の画像登録に問題が発生しております。

[ 大まかな状況 ]
・制作しているものはPHP WEBアプリケーションで、問題が発生しているのは画像を登録する処理。

・登録させる画像はファイルサイズ、幅、高さが一定の値 (縦160px 横160px) を超えた場合は圧縮する処理を加える。

・登録させる画像はサーバ内のディレクトリにファイルとして保存するのではなく、
 pg_escape_bytea関数でエスケープさせ、データベースに登録させる。

・特定の画像を登録させると
Warning: pg_query() [function.pg-query]: Query failed: server closed the
connection unexpectedly This probably means the server terminated abnormally
before or while processing the request. in
/usr/local/apache2/htdocs/mypage/a_profile_image_comp.php on line 193

のエラーが発生し、登録できない。
なお、このエラーはトランザクション処理の終了時
pg_query($resource_id,"commit;");
の行 (line 193) で発生している。


・すべての画像が登録が出来ないのではなく、一部の画像のみ。別の画像は問題なく登録できる。
ファイルの容量や幅、高さの問題ではない模様。

(例)
・タテに長い画像 (タテ 640px ヨコ 90px) (64kbyte)
・ヨコに長い画像 (タテ 90px ヨコ 640px)  (64kbyte)
 ・タテ、横ともに大きい画像 (タテ 320px ヨコ 180px)  (67kbyte)
・タテ、横ともに小さい画像 (タテ 80px ヨコ 80px)  (7kbyte)

上記のファイルは、いずれも問題なく登録可能。

登録できなかった画像は以下のサイズ
縦 157px 横 160px (7kbyte)
縦 160px 横 120px (3.5kbyte)

・また、これら登録できない画像も、初回登録時(つまりinsert)は上記エラーが発生するが、
 上書きするとき(update)の場合はエラーは発生せず、正常に登録完了できる。

・pgpool-IIを使用しない場合はこのエラーは発生しない。

======================================================================
[ 環境 ]
OS:      Cent OS 4.4
Apache:    httpd-2.0.59
PHP:       4.4.5
PostgreSQL  8.1.4

[PHPインストール時オプション]
'./configure' \
'--with-apxs2=/usr/local/apache2/bin/apxs' \
'--with-gd' \
'--with-jpeg-dir=/usr' \
'--with-zlib-dir=/usr' \
'--with-png-dir=/usr' \
'--with-openssl' \
'--enable-calendar' \
'--enable-mbstring' \
'--enable-mbregex' \
'--enable-zend-multibyte' \
'--enable-ftp' \
'--with-mcrypt' \


データベースは実機一台にポートで分けて三つを起動させている状態。
[PostgreSQL起動時コマンド]
PGPORT=5432 pg_ctl -D /usr/local/pgsql/data start
PGPORT=5433 pg_ctl -D /usr/local/pgsql/data.1 start
PGPORT=5434 pg_ctl -D /usr/local/pgsql/data.2 start

[pgpool起動時コマンド]
/usr/local/pgpool/bin/pgpool -d -n &


[pgpool-II pgpool.conf]

listen_addresses = 'localhost'
port = 9999
pcp_port = 9898
socket_dir = '/tmp'
pcp_socket_dir = '/tmp'
backend_socket_dir = '/tmp'
pcp_timeout = 10
num_init_children = 32
max_pool = 4
child_life_time = 300
connection_life_time = 0
child_max_connections = 0
logdir = '/tmp'

replication_mode = false
replication_strict = true
replication_timeout = 5000
load_balance_mode = false
replication_stop_on_mismatch = false
reset_query_list = 'ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT'
print_timestamp = true
master_slave_mode = false
connection_cache = true
health_check_timeout = 20
health_check_period = 0
health_check_user = 'nobody'
insert_lock = false
ignore_leading_white_space = false
log_statement = false

parallel_mode = true
enable_query_cache = false

pgpool2_hostname = 'localhost'
system_db_hostname = 'localhost'
system_db_port = 5432
system_db_dbname = 'pgpool'
system_db_schema = 'pgpool_catalog'
system_db_user = 'postgres'
system_db_password = ''

backend_hostname0 = ''
backend_port0 = 5432
backend_weight0 = 1
backend_hostname1 = ''
backend_port1 = 5433
backend_weight1 = 1
backend_hostname2 = ''
backend_port2 = 5434
backend_weight2 = 1



[ 分割テーブル定義登録 ]
insert into pgpool_catalog.dist_def values(
'■■■', ←DB名は伏せさてあります。
'public',
'c_profile_image',
'c_image_id',
ARRAY['c_image_id','filename','bin','r_datetime','type','c_member_id'],
ARRAY['INTEGER','TEXT','BYTEA','TIMESTAMP','TEXT','INTEGER'],
'pgpool_catalog.dist_def_c_profile_image')


[ 分割ルールファンクション ]
CREATE OR REPLACE FUNCTION pgpool_catalog.dist_def_c_profile_image(val
ANYELEMENT) RETURNS INTEGER AS $$
SELECT CASE WHEN $1 > 0 then 1
END $$ LANGUAGE SQL;


[ pgpool-IIのデバッグログ ]
・エラー発生時前後のログ

2007-05-22 12:37:56 DEBUG: pid 13474: read_kind_from_backend: read kind from
0 th backend C NUM_BACKENDS: 3
2007-05-22 12:37:56 DEBUG: pid 13474: read_kind_from_backend: read kind from
1 th backend C NUM_BACKENDS: 3
2007-05-22 12:37:56 DEBUG: pid 13474: read_kind_from_backend: read kind from
2 th backend C NUM_BACKENDS: 3
2007-05-22 12:37:56 DEBUG: pid 13474: pool_process_query: kind from backend:
C
2007-05-22 12:37:56 DEBUG: pid 13474: read_kind_from_backend: read kind from
0 th backend Z NUM_BACKENDS: 3
2007-05-22 12:37:56 DEBUG: pid 13474: read_kind_from_backend: read kind from
1 th backend Z NUM_BACKENDS: 3
2007-05-22 12:37:56 DEBUG: pid 13474: read_kind_from_backend: read kind from
2 th backend Z NUM_BACKENDS: 3
2007-05-22 12:37:56 DEBUG: pid 13474: pool_process_query: kind from backend:
Z
2007-05-22 12:37:56 DEBUG: pid 13474: pool_read_message_length: slot: 0
length: 5
2007-05-22 12:37:56 DEBUG: pid 13474: pool_read_message_length: slot: 1
length: 5
2007-05-22 12:37:56 DEBUG: pid 13474: pool_read_message_length: slot: 2
length: 5
2007-05-22 12:37:56 DEBUG: pid 13474: ReadyForQuery: message length: 5
2007-05-22 12:37:56 DEBUG: pid 13474: ReadyForQuery: transaction state: I
2007-05-22 12:37:56 DEBUG: pid 13474: pool_connection_pool_timer: set close
time 1179805076
2007-05-22 12:37:56 DEBUG: pid 13471: pool_rewrite_stmt: XXX rule 701
*** glibc detected *** free(): invalid next size (normal): 0x082a8108 ***
2007-05-22 12:37:57 DEBUG: pid 13342: reap_handler called
2007-05-22 12:37:57 DEBUG: pid 13342: reap_handler: call wait3
2007-05-22 12:37:57 DEBUG: pid 13342: child 13471 exits with status 6 by
signal 6
2007-05-22 12:37:57 DEBUG: pid 13342: fork a new child pid 13510
2007-05-22 12:37:57 DEBUG: pid 13342: reap_handler: normally exited
2007-05-22 12:37:57 DEBUG: pid 13510: I am 13510
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: auth kind: 0
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: parameter status data
received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: backend key data received
2007-05-22 12:37:57 DEBUG: pid 13510: s_do_auth: transaction state: I


[ *** glibc detected *** free(): invalid next size (normal): 0x082a8108 ** 
について] 

どうもこのエラーが発生したときは、以下のログが必ず出ているようです。
*** glibc detected *** free(): invalid next size (normal): 0x082a8108 ** 
この行が気になったので、
glibcをyumで glibc-2.3.4-2.36 にアップグレードしましたが、結果は変わらずでした。 


それ以上のバージョン(glibc-2.5など)のインストールを試みましたが、rpmパッケージ等の
上書きインストールがすぐに出来なかったので、今のところはそのままバージョンです。 




[ PostgreSQLのログ ]
2007-05-22 12:35:17 JST LOG:  autovacuum: processing database "■■■"
2007-05-22 12:36:17 JST LOG:  autovacuum: processing database "■■■"
2007-05-22 12:37:17 JST LOG:  autovacuum: processing database "■■■"
2007-05-22 12:37:56 JST idle in transactionLOG:  unexpected EOF on client
connection
2007-05-22 12:38:49 JST LOG:  autovacuum: processing database "■■■"
2007-05-22 12:39:49 JST LOG:  autovacuum: processing database "■■■"

この問題が発生したときは、以下のエラーログが出ているようです。
2007-05-22 12:37:56 JST idle in transactionLOG:  unexpected EOF on client
connection


---------------------------------------------------------------
[ 登録時のPHPプログラム ]

[大まかな流れ]
・HTMLの入力フォームからファイルを受信
・受信したファイルを一時的にWEBアプリケーション内のディレクトリ用に保存
(これは/tmpディレクトリのことではなく、自前のディレクトリにさらに移動させる処理)
・保存された画像のファイルサイズ、幅、高さをチェックし、規定を超えていたら、圧縮処理
・画像ファイルをpg_escape_bytea関数でエスケープさせ、DBに登録


※一部変数名などが実際のものとは異なっております。
 ご了承ください。

<?php

$date=date("YmdHis");
$r_datetime=date("Y-m-d H:i:s");
$time = time();

//画像データを受け取る
$profile_image_name1 = $_FILES["profile_image1"]["name"];
$profile_image1 = $_FILES["profile_image1"]["tmp_name"];


//画像の種類を調べる(jpg)。
$imgInf1 = getimagesize("$profile_image1");
$img_type1 = $imgInf1[2];

if($img_type1 != 2){
 $_FILES["profile_image1"]["tmp_name"] = "";
}

//バイト数単位での圧縮をかける//////////////////////////
$i = 0;
if($_FILES['profile_image1']["tmp_name"] != ""){
 //まずバイト数を出す
 $imgFilesize1 = filesize("$profile_image1");
 //100000byteを超えていたら圧縮。超えていなければ無視
 if($imgFilesize1>100000){
 //画像IDを得る。
 $im_inp = ImageCreateFromJPEG($_FILES['profile_image1']['tmp_name']);
 $outputJpgURL = "temporary_img/1_".$date."_".$c_member_id."_image";
  while(true){
   $i++;

   clearstatcache();
   $assyuku = 95 - 10*($i - 1);
   $image = imagejpeg($im_inp,"$outputJpgURL",$assyuku);
   $outputFilesize = filesize("$outputJpgURL");
   if($outputFilesize < 100000 || $i==8){break;}
  }
 }else{
  move_uploaded_file($profile_image1,
"temporary_img/1_".$date."_".$c_member_id."_image");
 }
 //////////////////////////////////////////////////////////
 $tmp1 = "temporary_img/1_".$date."_".$c_member_id."_image";
}


//データベースに接続
require_once("../init/init_db.php");
$resource_id = db_connectioni();
//一時ファイルの名前を登録
if($tmp1 != ""){
 $sql = "insert into
c_profile_temp_image(filename,r_time)values('$tmp1','$time')";
 $res_sql = pg_query($resource_id,"$sql");
}

//JPEG以外ははじく。100KB以外もはじく
$imgInf1 = getimagesize("$tmp1");

$imgfilesize1 = filesize("$tmp1");

if($imgfilesize1>100000){
 $tmp1 = "";
}

//ピクセル単位での圧縮をかける//////////////////////////
if($tmp1 != ""){
 //画像サイズを出す
 $width1 = $imgInf1[0];
 $height1 = $imgInf1[1];
 if($width1>160 || $height1>160){
  //この画像の場合の適切なサイズを算出
  $w_loss1 = $width1 - 160;
  $h_loss1 = $height1 - 160;
  if($w_loss1 > $h_loss1){
   $base_width1 = 160;
   $base_height1 = floor($height1/($width1/160));
  }elseif($h_loss1 > $w_loss1){
   $base_height1 = 160;
   $base_width1 = floor($width1/($height1/160));
  }else{
   $base_width1 = 160;
   $base_height1 = 160;
  }
  $im_inp1 = imagecreatefromjpeg($tmp1);
  $im_out1 = imagecreatetruecolor($base_width1, $base_height1);
  $recipe_image1 =
imagecopyresampled($im_out1,$im_inp1,0,0,0,0,$base_width1,$base_height1,$width1,$height1);

  $image1 = imagejpeg($im_out1,$tmp1);//
  // 画像のアップロード(とりあえずアップしてからバイナリに変更アップが終わったら消す)
  move_uploaded_file($image1, $profile_image_name1);
  imagedestroy($im_inp1);
  imagedestroy($im_out1);
  //名前をつける(アップ先のパス)
  //$file_pass1="temporary_img/1_".$date."_".$c_member_id."_image";
  $file_pass1=$tmp1;//
 }else{
  $file_pass1 = $tmp1;//プロフィールはちがう
  $base_width1 = $width1;
  $base_height1 = $height1;
 }
 //ファイルを作る
 $fp1=fopen($file_pass1,"rb");
 $imgdata1=fread($fp1,filesize($file_pass1));
 fclose($fp1);
 //エスケープ
 $imgdata1=pg_escape_bytea($imgdata1);
 //ファイル名をつける
 $filename1 = "1_".$date."_".$c_member_id."_image";
 if($width1>160 || $height1>160){
  //ファイルの削除
  //unlink($file_pass1);
 }
}
@unlink($file_pass1);

///////////////////////////////////////////////////////////////////
//既に登録してあるファイル名とサイズを検索する
$sql1 = "select * from c_member where c_member_id='$c_member_id'";
$res_sql1 = pg_query($resource_id,"$sql1");
$arycol1 = pg_fetch_array($res_sql1);
$img1 = $arycol1['image_filename_1'];


$image_width1 = $arycol1['image_width1'];
$image_height1 = $arycol1['image_height1'];


//新規ファイルがなければ古いファイル名とサイズのまま上書き
if($filename1 == ""){
 $filename1 = $img1;
 $base_width1 = $image_width1;
 $base_height1 = $image_height1;
}

//トランザクションの開始
pg_query($resource_id,"begin;");//////////////////////////////////////////////


//テーブルのロック
pg_query($resource_id,"lock table c_profile_image in share row exclusive
mode");
//検索
$sql = "select max(c_image_id) from c_profile_image";
$res_sql = pg_query($resource_id,"$sql");
$arycol = pg_fetch_array($res_sql);
$max = $arycol['max'];
//echo "$max";
if(!$res_sql){
exit;
}
if($tmp1 != ""){
 //既に登録してあればupdate。なければinsert。
 if($img1 == ""){
  $max += 1;
  $sql = "insert into
c_profile_image(c_image_id,filename,bin,type,c_member_id,r_datetime)values('$max','$filename1','$imgdata1','$img_type1','$c_member_id','$r_datetime')";
  $res_sql = pg_query($resource_id,"$sql");
  if(!$res_sql || pg_affected_rows($res_sql) !=1){
  pg_query($resource_id,"rollback;");
  exit;
  }
 }else{
  $sql = "update c_profile_image set
filename='$filename1',bin='$imgdata1',type='$img_type1',r_datetime='$r_datetime'
where filename='$img1'";
  $res_sql = pg_query($resource_id,"$sql");
  if(!$res_sql || pg_affected_rows($res_sql) !=1){
  pg_query($resource_id,"rollback;");
  exit;
  }
 }
}

pg_query($resource_id,"commit;");/////////////////////////////////////////


?>

以上です。
どなたかこの問題の対策など、ご存知の方が見えましたら、
ご教授いただけると幸いです。

よろしくお願い申し上げます。


------------------
エニグマ   浅野

asano @ engm.com





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