首页 文章

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

提问于
浏览
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()替换为 = 0结果相同 . 我可以在捕获异常和回滚后正常使用$ dbh,但是新的CGI :: Session返回错误 . ------------------------------------------已添加2017/07/26 18: 47 JST

请给我你的建议 .

谢谢 .

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->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表示应该控制事务的方式)和 commit 在eval块中:

    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是如何制作的,也许你需要写一些你想念的提取方法......

    希望这可以帮助!!

相关问题