已解决:DBI catch 语句消失且 CGI::Session 卡住
Posted
技术标签:
【中文标题】已解决:DBI catch 语句消失且 CGI::Session 卡住【英文标题】:Solved: DBI cached statements gone and CGI::Session is stucked 【发布时间】:2017-12-23 14:07:48 【问题描述】:我正在使用 Apache2.2(worker)/mod_perl 2.0.4/Apache::DBI/CGI::Session 和 Firebird RDBMS。
我还编写了 CGI::Session::Driver::firebird.pm 来使用 Firebird RDBMS。 DB 连接由 Apache::DBI 汇集,并为 CGI::Session Handle=>$dbh 提供连接句柄。
数据库连接数等于工作进程数。
我在 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() 在 catch 异常后会失败...
1) 我在 CGI::Session->errstr() 上添加了 die 语句 我收到一个错误“new(): failed: load(): could't retrieve data: retrieve(): $sth->execute failed with error message” 2)我在 session->load() 之后刷新会话对象 如果 $session 有效,则将更改存储到 DB。 3) 我将 begin_work() 替换为 AutoCommit=0 结果是一样的。我可以在捕获异常和回滚后正常使用 $dbh,但是新的 CGI::Session 返回错误。 ------------------------------------------ 2017/07/26 18 新增: 47 日产
请给我你的建议。
谢谢。
【问题讨论】:
也许session->flush
会杀死会话?也许有人会从$sessid
中删除任何值?
$session->flush() 不会引发任何异常。在 $session->flush() 之后,另一个查询运行良好,但下一个“new CGI::Session()”返回空。 CGI::Session->load() 返回 undef。
我也通过删除 $session->flush() 对其进行了测试,但下一个 CGI:Session 返回为空。
你为什么又打电话给new CGI::Session()
?您是否需要在同一个脚本中检索不同的会话? ... $session->flush()
不会引发异常,除非您告诉它,您可以添加 $session->flush() or die "Unable to update session storage!"
并查看是否引发异常
示例代码不会单独引发错误。这是在web系统中,connect()语句在通用包和池连接中。因此,每个网页屏幕(cgi 程序)在开始时都会调用 new CGI::Session() 并检索 $session 并使用它进行处理。我的问题是“在某些屏幕上发生 sql 错误后,在所有屏幕上检索都失败”。
【参考方案1】:
在请求更改 CGI::Session::Driver::DBI.pm 之前,您可以尝试多种方法...
首先,更改new CGI::Session
的调用方式,以诊断是否在创建或加载会话时出现问题:
my $session = CGI::Session->new('dbi:firebird',$sessid,Handle=>$dbh) or die CGI::Session->errstr();
param
或 delete
方法将会话更改存储在 $session
句柄中,而不是在 DB 中。 flush
将会话句柄内所做的更改存储在 DB 中。仅在会话->参数设置/更新或会话删除后使用 $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 是如何制作的,也许您需要编写一些您缺少的获取方法...
希望这会有所帮助!
【讨论】:
1) 添加 CGI::Session->errstr(): new(): failed: load(): 无法检索数据:retrieve(): $sth->execute failed with error消息 2) 刷新会话对象:如果 $session 有效,则将更改存储到 DB。 3)使用 AutoCommit=0 和其他:情况相同。我可以在捕获异常和回滚后正常使用 $dbh,但是新的 CGI::Session 返回错误。 --- 我的 Driver/firebird.pm 只有 init() 和 table_name(),然后在 DBI.pm 中使用 store/retrieve。我写了 firebird.pm 来加载 DBD::Firebird 并将 DSN 设置为 'dbi:firebird'。 我在 firebird.pm 中添加了没有“_cached”的存储、检索、遍历。它看起来工作正常。 mysql.pm 有 mk_dsn,init,table_name,store。 store() 更改为使用 DBI.pm 中的“ON DUPLICATES”。 DBI->begin_work() 将 AutoCommit 更改为 0。我找到了解决方法。那么,我想知道为什么缓存语句不见了? 你能用你发现的错误更新你的问题吗?以上是关于已解决:DBI catch 语句消失且 CGI::Session 卡住的主要内容,如果未能解决你的问题,请参考以下文章