DBI缓存的语句消失了,CGI :: Session被卡住了

问题描述 投票:1回答:1

我正在使用Apache2.2(worker)/ mod_perl 2.0.4 / Apache :: DBI / CGI :: Session和Firebird RDBMS。

我还编写了CGI :: Session :: Driver :: firebird.pm来使用Firebird RDBMS。数据库连接由Apache :: DBI汇集,并提供CGI :: Session {Handle => $ dbh}的连接句柄。

DB连接数等于工作进程数。

我在3个月前发布了Programming with Apache::DBI and firebird. Get Stucked httpd on exception。我找到了这个问题的原因,并想知道如何解决它。

$dbh = DBI->connect("dbi:Firebird:db=$DBSERVER:/home/cdbs/xxnet.fdb;
ib_charset=UTF8;ib_dialect=3",$DBUSER,$DBPASS,{
    AutoCommit=>1,
    LongReadLen=>8192,
    RaiseError=>1
});
my $session = new CGI::Session('dbi:firebird',$sessid,{Handle=>$dbh});
my $ses_p1 = $session->param('p1');

eval { $dbh->begin_work()

  my $sql = "SELECT * FROM SAMPLETABLE"
  my $st = $dbh->prepare($sql);
  $st->execute();
  while (my $R = $st->fetchrow_hashref()) {
   ...
  }
  $st->finish();
}; warn $@ if $@;
if ($@) {
  $dbh->rollback();
}else{
  $dbh->commit();
}
$session->flush();

发生sql错误时,eval块会捕获异常和回滚事务。之后,CGI :: Session不再检索会话对象。

因为prepare_cached语句在CGI :: Session :: DBI.pm处失败。 CGI :: Session :: DBI.pm使用prepare_cached($ sql,undef,3)。 '3'是使用缓存语句的最安全的方法,但在这种情况下它永远不会找到破坏的语句。

如何解决这个问题?提出更改CGI :: Session :: DBI.pm的请求以使用prepare()语句?在firebird.pm中写store(),retrieve(),traverse()函数来使用prepare()语句?

捕获异常后,其他prepare_cached()可能会失败...


1)我在CGI :: Session-> errstr()上添加了die语句我收到错误“new():failed:load():无法检索数据:retrieve():$ sth-> execute failed with error message “2)如果$ session有效,我会在session-> load()之后刷新会话对象,更改存储到DB。 3)我将begin_work()替换为{AutoCommit} = 0结果相同。我可以在捕获异常和回滚后正常使用$ dbh,但是新的CGI :: Session返回错误。 ------------------------------------------已添加2017/07/26 18: 47 JST

请给我你的建议。

谢谢。

perl session firebird dbi mod-perl
1个回答
1
投票

在请求更改为CGI :: Session :: Driver :: DBI.pm之前,您可以尝试各种各样的事情......

首先,更改调用new CGI::Session的方式,以便在创建或加载会话时诊断问题是否发生:

my $session = CGI::Session->new('dbi:firebird',$sessid,{Handle=>$dbh}) or die CGI::Session->errstr();

方法paramdelete将更改存储在$session句柄内的会话中,而不是DB中。 flush在DB中存储会话句柄内的更改。仅在session-> param set / update或session delete之后使用$session->flush()

$session->param('p1','someParamValue');
$session->flush() or die 'Unable to update session storage!';

# OR
$session->delete();
$session->flush() or die 'Unable to update session storage!';

方法flush不会破坏$session手柄(你可以在冲洗后调用$session->param('p1'))。在某些情况下,mod_perl会缓存$session,导致下次尝试加载同一会话时出现问题。在这些情况下,它需要在不再需要时销毁:

undef($session)

我可以建议的最后一件事是避免使用begin_work方法,用AutoCommit控制交易行为(因为DBD::Firebird documentation说应该控制交易的方式)和eval块内的commit

eval {
    # Setting AutoCommit to 0 enables transaction behavior
    $dbh->{AutoCommit} = 0;

    my $sql = "SELECT * FROM SAMPLETABLE"
    my $st = $dbh->prepare($sql);
    $st->execute();

    while (my $R = $st->fetchrow_hashref()) {
        ...
        }

    $st->finish();
    $dbh->commit();
    };
if ($@) {
     warn "Tansaction aborted! $@";
     $dbh->rollback();
     }

# Remember to set AutoCommit to 1 after the eval
$dbh->{AutoCommit} = 1;

你说你为Firebird编写了自己的会话驱动程序......你应该看看CGI / Driver / sqlite.pm或CGI / Driver / mysql.pm是如何制作的,也许你需要写一些你想念的提取方法......

希望这可以帮助!!

© www.soinside.com 2019 - 2024. All rights reserved.