DBD/DBI: 프로그램이 포크되면 충돌이 발생합니다.

DBD/DBI: 프로그램이 포크되면 충돌이 발생합니다.

$crash 매개변수가 설정되지 않은 경우 다음 프로그램이 실행됩니다.

$ perl example mysql://:tange@/tange/mytable
dburl mysql://:tange@/tange/mytable
databasedriver mysql user  password tange host  port  database tange table mytable query 
run DROP TABLE IF EXISTS mytable;
run CREATE TABLE mytable
                (Seq INT,
                 Exitval INT
                 );
run INSERT INTO mytable (Seq,Exitval) VALUES (?,?);
run INSERT INTO mytable (Seq,Exitval) VALUES (?,?);

$crash가 설정된 경우 bzip2는 open3을 통해 실행되어 포크된 프로세스를 통해 데이터를 전송하며 이로 인해 DBD/DBI가 충돌할 수 있습니다.

$ perl example mysql://:tange@/tange/mytable 1
dburl mysql://:tange@/tange/mytable
databasedriver mysql user  password tange host  port  database tange table mytable query 
run DROP TABLE IF EXISTS mytable;
run CREATE TABLE mytable
                (Seq INT,
                 Exitval INT
                 );
run INSERT INTO mytable (Seq,Exitval) VALUES (?,?);
Orig:
As bzip2:BZh9rE8P�
1
run INSERT INTO mytable (Seq,Exitval) VALUES (?,?);
DBD::mysql::st execute failed: MySQL server has gone away at example line 157.
DBD::mysql::st execute failed: MySQL server has gone away at example line 157.

Postgresql을 사용하는 경우에도 마찬가지입니다.

$ perl example pg:////mytable 
dburl pg:////mytable
databasedriver pg user  password  host  port  database  table mytable query 
run DROP TABLE IF EXISTS mytable;
run CREATE TABLE mytable
                (Seq INT,
                 Exitval INT
                 );
run INSERT INTO mytable (Seq,Exitval) VALUES (?,?);
run INSERT INTO mytable (Seq,Exitval) VALUES (?,?);

그리고 $crash를 설정합니다.

$ perl example pg:////mytable 1
dburl pg:////mytable
databasedriver pg user  password  host  port  database  table mytable query 
run DROP TABLE IF EXISTS mytable;
run CREATE TABLE mytable
                (Seq INT,
                 Exitval INT
                 );
run INSERT INTO mytable (Seq,Exitval) VALUES (?,?);
Orig:
As bzip2:BZh9rE8P�
1
run INSERT INTO mytable (Seq,Exitval) VALUES (?,?);
DBD::Pg::st execute failed: server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request. at example line 157.
DBD::Pg::st execute failed: server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request. at example line 157.

왜? 해결책이 있나요?

나에게 open3과 포크는 DBD/DBI와 전혀 관련이 없습니다.


#!/usr/bin/perl

use IPC::Open3;

my $sql = SQL->new(shift);
my $crash = shift;
$Global::debug = "all";
$sql->create_table();
$sql->insert_records(2);
$crash and print length string_zip("abc"),"\n";
$sql->insert_records(3);

sub string_zip {
    # Pipe string through 'cmd'
    my $cmd = shift;
    my($zipin_fh, $zipout_fh,@base64);
    ::open3($zipin_fh,$zipout_fh,">&STDERR","bzip2 -9");
    if(fork) {
    close $zipin_fh;
    @base64 = <$zipout_fh>;
    close $zipout_fh;
    } else {
    close $zipout_fh;
    print $zipin_fh @_;
    close $zipin_fh;
    exit;
    }
    ::debug("zip","Orig:@_\nAs bzip2:@base64\n");
    return @base64;
}

sub undef_if_empty {
    if(defined($_[0]) and $_[0] eq "") {
    return undef;
    }
    return $_[0];
}

sub debug {
    # Uses:
    #   $Global::debug
    #   %Global::fd
    # Returns: N/A
    print @_[1..$#_];
}

package SQL;

sub new {
    my $class = shift;
    my $dburl = shift;
    $Global::use{"DBI"} ||= eval "use DBI; 1;";
    my %options = parse_dburl($dburl);
    my %driveralias = ("sqlite" => "SQLite",
               "sqlite3" => "SQLite",
               "pg" => "Pg",
               "postgres" => "Pg",
               "postgresql" => "Pg");
    my $driver = $driveralias{$options{'databasedriver'}} || $options{'databasedriver'};
    my $database = $options{'database'};
    my $host = $options{'host'} ? ";host=".$options{'host'} : "";
    my $port = $options{'port'} ? ";port=".$options{'port'} : "";
    my $dsn = "DBI:$driver:dbname=$database$host$port";
    my $userid = $options{'user'};
    my $password = $options{'password'};;
    my $dbh = DBI->connect($dsn, $userid, $password, { RaiseError => 1 })
    or die $DBI::errstr;
    return bless {
    'dbh' => $dbh,
    'max_number_of_args' => undef,
    'table' => $options{'table'},
    }, ref($class) || $class;
}

sub parse_dburl {
    my $url = shift;
    my %options = ();
    # sql:mysql://[[user][:password]@][host][:port]/[database[/table][?sql query]]

    if($url=~m!(?:sql:)? # You can prefix with 'sql:'
               ((?:oracle|ora|mysql|pg|postgres|postgresql)(?:s|ssl|)|
                 (?:sqlite|sqlite2|sqlite3)):// # Databasedriver ($1)
               (?:
                ([^:@/][^:@]*|) # Username ($2)
                (?:
                 :([^@]*) # Password ($3)
                )?
               @)?
               ([^:/]*)? # Hostname ($4)
               (?:
                :
                ([^/]*)? # Port ($5)
               )?
               (?:
                /
                ([^/?]*)? # Database ($6)
               )?
               (?:
                /
                ([^?]*)? # Table ($7)
               )?
               (?:
                \?
                (.*)? # Query ($8)
               )?
              !ix) {
    $options{databasedriver} = ::undef_if_empty(lc(uri_unescape($1)));
    $options{user} = ::undef_if_empty(uri_unescape($2));
    $options{password} = ::undef_if_empty(uri_unescape($3));
    $options{host} = ::undef_if_empty(uri_unescape($4));
    $options{port} = ::undef_if_empty(uri_unescape($5));
    $options{database} = ::undef_if_empty(uri_unescape($6));
    $options{table} = ::undef_if_empty(uri_unescape($7));
    $options{query} = ::undef_if_empty(uri_unescape($8));
    ::debug("sql","dburl $url\n");
    ::debug("sql","databasedriver ",$options{databasedriver}, " user ", $options{user},
          " password ", $options{password}, " host ", $options{host},
          " port ", $options{port}, " database ", $options{database},
          " table ",$options{table}," query ",$options{query}, "\n");

    } else {
    ::error("$url is not a valid DBURL");
    exit 255;
    }
    return %options;
}

sub uri_unescape {
    # Copied from http://cpansearch.perl.org/src/GAAS/URI-1.55/URI/Escape.pm
    # to avoid depending on URI::Escape
    # This section is (C) Gisle Aas.
    # Note from RFC1630:  "Sequences which start with a percent sign
    # but are not followed by two hexadecimal characters are reserved
    # for future extension"
    my $str = shift;
    if (@_ && wantarray) {
    # not executed for the common case of a single argument
    my @str = ($str, @_);  # need to copy
    foreach (@str) {
        s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
    }
    return @str;
    }
    $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $str;
    $str;
}

sub run {
    my $self = shift;
    my $stmt = shift;
    my $dbh = $self->{'dbh'};
    ::debug("sql","run $stmt\n");
    # Execute with the rest of the args - if any
    my $rv;
    my $sth;
    $sth = $dbh->prepare($stmt);
    $rv = $sth->execute(@_);
    return $sth;
}

sub table {
    my $self = shift;
    return $self->{'table'};
}

sub create_table {
    my $self = shift;
    my $table = $self->table();
    $self->run(qq(DROP TABLE IF EXISTS $table;));
    $self->run(qq{CREATE TABLE $table
        (Seq INT,
         Exitval INT
         }.
           qq{);});
}

sub insert_records {
    my $self = shift;
    my $seq = shift;
    my $record_ref = shift;
    my $table = $self->table();
    $self->run("INSERT INTO $table (Seq,Exitval) ".
           "VALUES (?,?);", $seq, -1000);
}

답변1

하위 또는 상위 프로세스가 종료되면 해당 데이터베이스 핸들 및 관련 소켓을 닫고, 서버 측에서는 해당 백엔드도 종료됩니다.

그 시점부터 다른(아직 존재하는) 클라이언트 프로세스가 데이터베이스 핸들을 사용하려고 하면 쿼리 전송이 실패합니다.MySQL 서버가 사라졌습니다아니면 포스트그레스를 사용하세요서버가 예기치 않게 연결을 종료했습니다.. 메시지는 무슨 일이 일어났는지에 대한 매우 정확한 설명인 것 같습니다.

주요 해결 방법은 어떤 방식으로든 데이터베이스 핸들을 공유 DBI->connect()하지 않고 프로세스 간에 호출하는 것입니다.fork

데이터베이스 활동이 상위 데이터베이스로 제한되는 경우 AutoInactiveDestroy사전에 데이터베이스 핸들을 설정할 수 있습니다(DBI 1.614부터). 그러면 InactiveDestroy하위 항목에 자동으로 설정되어 문제가 해결됩니다. InactiveDestroyDBI 문서를 참조하세요 :

데이터베이스 핸들의 경우 이 속성은 연결 끊기 메서드에 대한 명시적 호출을 비활성화하지 않고 핸들이 여전히 "활성"으로 표시된 동안 발생하는 DESTROY에 대한 암시적 호출만 비활성화합니다.

이 속성은 하위 프로세스를 "포크"하는 Unix 응용 프로그램을 위해 특별히 설계되었습니다. 일부 드라이버의 경우 하위 프로세스가 종료될 때 상속된 핸들이 삭제되면 상위 프로세스의 해당 핸들이 작동을 중지합니다.

상위 프로세스 또는 하위 프로세스(둘 다는 아님)의 모든 공유 핸들에서 InactiveDestroy가 true로 설정되어야 합니다. 또는 연결 시 상위 항목에서 "AutoInactiveDestroy"를 설정할 수 있는 것이 바람직합니다.

관련 정보