web-dev-qa-db-ja.com

SQLAlchemyで接続プールをPgBouncerトランザクションレベルのプールに最適に使用する方法

SQLAlchemyを使用してPgBouncerの背後にあるPostgreSQLデータベースにクエリを実行し、トランザクションレベルのプールを使用します。

この種のセットアップに使用するのに最適なパターンは何ですか? ConnectionPoolを使用してプロセスごとに1つのエンジンが必要ですか、またはリクエストごとにエンジンを作成し、それぞれにNullPoolを使用する必要がありますか?一緒に使用する必要がある別のパターンはありますか?

どうもありがとう!さらに情報が必要な場合はお知らせください。できるだけ早く更新します。

16

pGBouncerを使用する場合は、NullPoolのみを使用することをお勧めします。その場合、サブプロセスの境界を越えてソケット接続は行われないため、サブプロセス間で単一のエンジンを共有できる場合があります。ただし、この境界を越えて、アクティブなトランザクションを持つセッションなど、接続オブジェクトを参照するものを共有することはできません。 「engine-per-request」を実行したくはないはずですが、エンジンは高価なオブジェクトであり、特定のデータベースのURLを初めて表示したときに、そのURLに関する多くの情報を蓄積します。

9
zzzeek

アプリケーション名を設定

多くのプロセスを実行する予定がある場合は、それらがどこから接続しているかを知る必要があります。 PGBouncerは、これを_pg_stat_activity_から見えなくします。これを解決するには、_application_name_に必要な情報を慎重に設定します。

_# Sets the application name for this connection in the form of
#   application-name:user@Host
prog = os.path.basename(sys.argv[0]) or 'desjob'
username = pwd.getpwuid (os.getuid ()).pw_name
hostname = socket.gethostname().split(".")[0]·
args.setdefault('connect_args', {'application_name': "%s:%s@%s" %
    (prog, username, hostname)})
args.setdefault('isolation_level', "AUTOCOMMIT")
engine = create_engine(url, **args)
_

優先セッション

Engineオブジェクトからのリクエストは複数の接続を生成して保持できるため、セッションを使用します。 Postgresへの接続はそれほど高価ではありません。PGBouncerを使用すれば、それよりもさらに安価です。私は常にNullPoolを使用するので、Postgresに表示される接続は、実際に使用されている接続のみです。

_from sqlalchemy.pool import Pool, NullPool
engine = create_engine(uri, poolclass=NullPool)
_

アイドルトランザクションの削除

PGBouncerを使用してスケーリングすることが目的の場合は、トランザクションを開いたままにしないでください。これを行うには、autocommitonをオンにする必要があります。これはSQLAlchemyでは単純ではありません... "autocommit"と呼ばれるものを設定できる場所は3つあります:

psycopg2オートコミット

_conn = psycopg2.connect(uri)
conn.autocommit = True
_

SQLAlchemyはその下で何が起こっているのかを知る必要があるため、安全でないと考えられます。

セッションの自動コミット

_Session = sessionmaker(bind=engine, autocommit=True)
session = Session()
_

これには、注意深く明示的な処理が必要です。

_session.begin()
session.execute(...)
session.rollback()
_

begin()commit()はネストできないため、関数の呼び出しと例外の処理は非常に困難です。

_def A():
  session.begin()
  ...
  session.rollback()

def B():
  session.begin()
  try:
      A() # error, already open
_

このモードでは、psycopg2 autocommitFalse(デフォルト)のように見えます

エンジンの自動コミット

エンジンの作成時にエンジン分離モードを_"AUTOCOMMIT"_に設定すると、既存のコードを変更する必要がない新しいデフォルトの動作が確立されます。

_engine = create_engine(uri, isolation_level="AUTOCOMMIT")
_

このモードでは、psycopg2 autocommitTrueのように見えます

ここでの主な問題は、コードブロックがトランザクションにラップされることを保証する唯一の方法は、ステートメントを手動で発行することであるということです。

_session.execute("BEGIN")
#...
session.execute("COMMIT")
_
4
eradman