Perl Mojolicious + PgAsync插件 - 泄漏数据库连接?

时间:2013-11-27 21:01:37

标签: perl postgresql mojolicious event-stream

我正在使用名为pgAsync的插件 Mojolicious ,以便侦听从 Postgres 数据库发出的NOTIFY个事件。我目前的代码工作正常,但我发现数据库连接的数量只会不断增加。

后端

#!/usr/bin/env perl

use Mojolicious::Lite;
use Mojo::IOLoop;
use Mojolicious::Plugin::PgAsync;

app->secret('awdawdawdawd');

plugin PgAsync => {dbi => ['dbi:Pg:dbname=;host=;port=;', '', '', {AutoCommit => 0, RaiseError => 1}]};

any '/api/listen' => sub {
  my $self = shift;

  my $saved_tx = $self->tx;

  Mojo::IOLoop->stream($self->tx->connection)->timeout(300);

  $self->res->headers->add('Content-Type' => 'text/event-stream');
  $self->res->headers->add('Cache-Control' => 'no-cache');
  $self->res->headers->add('Access-Control-Allow-Origin' => '*');

  # required for IE
  $self->write(" " x 2048);
  $self->write("\nretry: 2000\n\n");

  my $drain_cb;
  $drain_cb = sub {
    my $c = shift;
    $c->render_later;
    $c->tx($saved_tx);
    $c->pg_listen('foo', sub {
      my $notify = shift;
      my $payload = $notify->{payload};
      $c->write("id:1\ndata:$payload\n\n", $drain_cb);
    });
  };

  $self->pg_listen('foo', sub {
    my $notify = shift;
    my $payload = $notify->{payload};
    $self->tx($saved_tx);
    $self->write("id:1\ndata:$payload\n\n", $drain_cb);
  });

};

app->start;

正如你所看到的,我需要做一些hackery才能让它按照我的意愿运行。 pg_listen似乎在每次通话后都没有取消回调。当我再次调用pg_listen时(由于它未定义),这导致错误,我不得不修改PgAsync::Db.pm的第34行以添加对此的检查:

$self->callback->($notify_hash, $notify_hash);

- > TO->

$self->callback->($notify_hash, $notify_hash) if defined $self->callback

前端

我正在使用带有 EventSource 对象的JavaScript来侦听来自Mojolicious脚本的推送通知:

var es = new EventSource("/api/listen");
var listener = function (event) {
  console.log(event.data);
};
es.addEventListener("open", listener);
es.addEventListener("message", listener);
es.addEventListener("error", listener);

问题

仔细观察,似乎这个系统不断创建更多的数据库连接:

  1. 从无到有,有1个数据库连接,我在这里查看select count(*) from pg_stat_activity;

  2. 的数据库连接数量
  3. 我用DEBUG_PG=1 morbo mojopush.pl开始morbo和Mojolicious脚本。现在有2个数据库连接(第一个是psql,第二个是morbo)

  4. 我用EventSource对象打开网页。它使用GET的内容类型正确设置text/event-stream请求,并保持连接处于打开状态。现在有3个数据库连接

  5. 我进入psql并发送NOTIFY foo, 'test',Mojo脚本检测到它,网页显示'test。仍然有3个数据库连接。

  6. 我刷新页面,现在有4个数据库连接。我等待300秒的非活动超时,然后创建另一个数据库连接,从而产生5个连接。

  7. 如果有人能帮助我指出正确的方向,那将非常感激!

2 个答案:

答案 0 :(得分:1)

不是100%肯定所以根据提供的信息这是一个猜测,但我想知道每个页面加载是否正在启动新的数据库连接,然后正在侦听通知。如果是这种情况,我想知道数据库连接是否已从池中有效删除,因此会在下一页加载时创建。

如果是这种情况,我的建议是拥有一个专门用于侦听通知的单独DBI数据库句柄,以便它们在队列中不活动。这可能在您的页面工作流程之外完成。

答案 1 :(得分:1)

我知道这是迟到的答案,但我与Mojolicious的关系不超过两周。这个问题似乎没有得到解决,我对可能分享的PgAsync有一些看法。

我在使用PgAsync进行一些练习后用谷歌搜索了这篇文章。我也可以观察到“连接泄漏”,而不是监听/通知案例,而是使用“pg”帮助程序执行一系列独立查询,类似于此处给出的方案:https://groups.google.com/forum/#!topic/mojolicious/titaWRImLt0

特别是,在发出多个同时请求时发生泄漏。同时,我可以看到两个同时连续运行请求循环的一些请求,还有未完成的查询执行和超时。

仍然没有从插件机制中理解太多,并且通常是Mojo :: event hadling,我猜测可能是一个刚刚获得免费的数据库连接可能会被选择用于另一个请求。

因此,在一些或多或少的盲目黑客之后,我试图在数据库连接完成其请求和返回空闲池之间引入一个愚蠢的延迟。我是通过改变这行代码来实现的:

https://metacpan.org/source/ROMANENKO/Mojolicious-Plugin-PgAsync-0.03/lib/Mojolicious/Plugin/PgAsync/Db.pm#L49

为:

$reactor->timer(1 => sub { $self->make_free->($self) });

在更改之后,连接泄漏和未完成的请求都消失了。这绝对不是解决方法,而是一种解决方法。也许这也适用于你的情况。

但是,当查询正在执行时客户端断开连接( Ctrl + C )时,我可以看到仍然存在连接泄漏。这种情况使数据库连接在db_pool处“孤立”,并且永远不会返回到db_free。

除非作者更快地获得它,否则我希望深入了解插件的代码,找到真正的修复,并可能添加一些增强功能。在我目前的工作中,我需要这个插件。