Wie verwende ich das Verbindungspooling in SQLAlchemy am besten für das Pooling auf PgBouncer-Transaktionsebene?

15

Verwenden von SQLAlchemy zum Abfragen einer PostgreSQL-Datenbank hinter PgBouncer unter Verwendung von Pooling auf Transaktionsebene.

Was ist das beste Muster für diese Art der Einrichtung? Sollte ich ein Modul pro Prozess haben, ein verwenden ConnectionPool, oder sollte ich ein Modul pro Anfrage erstellen und NullPoolfür jedes von ihnen verwenden? Gibt es ein anderes Muster, das ich verwenden sollte?

Vielen Dank! Lassen Sie mich wissen, wenn weitere Informationen benötigt werden und ich werde so schnell wie möglich aktualisieren.

Juan Carlos Coto
quelle

Antworten:

9

Mit PGBouncer möchten Sie wahrscheinlich nur bei NullPool bleiben. In diesem Fall können Sie möglicherweise eine einzelne Engine über Unterprozesse hinweg gemeinsam nutzen, da keine Socket-Verbindungen über die Unterprozessgrenze übertragen werden. Sie können jedoch keine Verweise auf ein Verbindungsobjekt wie eine Sitzung mit einer aktiven Transaktion über diese Grenze freigeben. Sie möchten auf keinen Fall "Engine-per-Request" ausführen. Eine Engine ist ein teures Objekt, das beim ersten Aufrufen einer bestimmten Datenbank-URL viele Informationen sammelt.

zzzeek
quelle
4

Legen Sie den Anwendungsnamen fest

Wenn Sie davon ausgehen, dass viele Prozesse ausgeführt werden, müssen Sie wissen, von wo aus sie eine Verbindung herstellen. PGBouncer wird dies unsichtbar machen pg_stat_activity. Lösen Sie dieses Problem, indem application_nameSie die folgenden Informationen sorgfältig festlegen:

# 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)

Sitzungen bevorzugen

Verwenden Sie Sitzungen, da Anforderungen von einem Engine-Objekt mehrere Verbindungen erzeugen und beibehalten können. Das Herstellen einer Verbindung zu Postgres ist nicht sehr teuer, mit PGBouncer sogar noch weniger. Ich würde immer NullPoolso verwenden, dass die einzigen Verbindungen, die Sie in Postgres sehen, die Verbindungen sind, die tatsächlich verwendet werden.

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

Beseitigen Sie inaktive Transaktionen

Wenn Sie PGBouncer zum Skalieren verwenden möchten, müssen Sie unbedingt vermeiden, dass Transaktionen offen bleiben. Um dies zu tun müssen Sie drehen autocommit auf . Dies ist mit SQLAlchemy nicht einfach. Es gibt drei Stellen, an denen ein sogenanntes "Autocommit" festgelegt werden kann:

psycopg2 autocommit

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

Wird als unsicher eingestuft, da SQLAlchemy wissen muss, was darunter vor sich geht.

Session Autocommit

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

Dies erfordert eine sorgfältige, explizite Übergabe:

session.begin()
session.execute(...)
session.rollback()

Funktionsaufruf und Ausnahmegabe ist außerordentlich schwierig , da begin()und commit()kann nicht verschachtelte:

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

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

In diesem Modus autocommitscheint psycopg2 zu sein False(Standardeinstellung)

Motor-Autocommit

Durch das Festlegen des Engine-Isolationsmodus "AUTOCOMMIT"beim Erstellen der Engine wird ein neues Standardverhalten festgelegt, das möglicherweise keine Änderungen an vorhandenem Code erfordert.

engine = create_engine(uri, isolation_level="AUTOCOMMIT")

In diesem Modus autocommitscheint psycopg2 zu seinTrue

Das Hauptproblem hierbei ist, dass die einzige Möglichkeit, sicherzustellen, dass ein Codeblock in eine Transaktion eingeschlossen wird, darin besteht, die Anweisungen manuell zu senden:

session.execute("BEGIN")
#...
session.execute("COMMIT")
Eradman
quelle