1

Postgresデータベースから発行されているイベントをリッスンするために、pgAsyncというプラグインでMojoliciousを使用しています。私が現在持っているコードは正常に動作していますが、データベース接続の数は増え続けています。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;

ご覧のとおり、これを希望どおりに機能させるには、ちょっとしたハッカーを行う必要がありました。 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

フロントエンド

Mojolicious スクリプトからのプッシュ通知をリッスンするために、 EventSourceオブジェクトで JavaScript を使用しています。

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. morbo と Mojolicious スクリプトを で開始しDEBUG_PG=1 morbo mojopush.plます。現在、2 つのデータベース接続があります (1 つ目は psql の me で、2 つ目は morbo です)。

  3. EventSource オブジェクトを含む Web ページを開きます。GETcontent-type でリクエストを正しく設定text/event-streamし、接続を開いたままにします。現在、3 つのデータベース接続があります

  4. psql に入って sendを実行NOTIFY foo, 'test'すると、Mojo スクリプトがそれを検出し、Web ページに「test. まだ 3 つの db 接続があります。

  5. ページを更新すると、現在 4 つのデータベース接続があります。300 秒の非アクティブ タイムアウトを待つと、さらに別のデータベース接続が作成され、5 つの接続が発生します。

誰かが私を正しい方向に向けるのを助けることができれば、それは非常にありがたいです!

4

2 に答える 2

1

これが遅い答えであることは知っていますが、私はモジョリシャスと一緒に2週間以内です. この問題は未解決のようです。共有できる PgAsync についていくつかの観察があります。

PgAsync でいくつかの演習を行った後、グーグルでこの投稿を見つけました。また、リッスン/通知の場合ではなく、「pg」ヘルパーを使用して一連の独立したクエリを実行することで、「接続リーク」を観察することもできました: https://groups. google.com/forum/#!topic/mojolicious/titaWRImLt0 .

特に、複数の同時リクエストを行うとリークが発生していました。同時に、同時に継続的に実行されている 2 つのリクエスト ループのリクエストの一部が、未完了のクエリ実行とタイムアウトのままになっていることがわかりました。

プラグインの仕組みや一般的な Mojo:: イベント ハドリングについてはまだあまり理解していませんが、解放されたばかりのデータベース接続が別のリクエストに対してすぐに選択されるのではないかと推測しました。

そこで、多かれ少なかれ盲目的なハッキングを行った後、データベース接続がリクエストを終了してから空きプールに戻るまでの間に、ばかげた遅延を導入しようとしました。このコード行を変更してそれを行いました:

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 に戻ることはありません。

作成者がすぐに解決しない限り、プラグインのコードを深く掘り下げて真の修正を見つけ、機能強化を追加したいと考えています。私は現在の仕事でこのプラグインをたくさん必要としています。

于 2014-01-07T11:35:10.020 に答える
1

100% 確実ではないため、提供された情報に基づいた推測にすぎませんが、ページの読み込みごとに新しいデータベース接続が開始され、通知をリッスンしているのではないでしょうか。この場合、データベース接続がプールから効果的に削除され、次のページの読み込み時に作成されるのではないかと思います。

この場合、私の推奨事項は、通知をリッスンする専用の別の DBI データベース ハンドルを用意して、通知がキューでアクティブにならないようにすることです。これは、ページ ワークフローの外で行われる場合があります。

于 2013-12-20T09:09:33.143 に答える