it-swarm.com.de

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

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

Was ist das beste Muster für diese Art der Einrichtung? Sollte ich eine Engine pro Prozess mit einem ConnectionPool haben oder sollte ich eine Engine pro Anforderung erstellen und NullPool für jede einzelne 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.

16

mit PGBouncer möchten Sie wahrscheinlich einfach bei NullPool bleiben. In diesem Fall können Sie möglicherweise eine einzelne Engine für mehrere Unterprozesse freigeben, da keine Socket-Verbindungen über die Unterprozessgrenze übertragen werden. Sie können jedoch nichts, was sich auf ein Verbindungsobjekt bezieht, wie z. B. eine Sitzung mit einer aktiven Transaktion, über diese Grenze hinweg freigeben. Sie möchten auf keinen Fall "Engine-per-Request" ausführen. Eine Engine ist ein teures Objekt, das beim ersten Anzeigen viele Informationen zu einer bestimmten Datenbank-URL sammelt.

9
zzzeek

Legen Sie den Anwendungsnamen fest

Wenn Sie erwarten, dass viele Prozesse ausgeführt werden, müssen Sie wissen, von wo aus sie eine Verbindung herstellen. PGBouncer macht dies für pg_stat_activity Unsichtbar. Lösen Sie dies, indem Sie den application_name Sorgfältig mit den Informationen einstellen, die Sie benötigen:

# Sets the application name for this connection in the form of
#   application-name:[email protected]
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:%[email protected]%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 NullPool verwenden, damit 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)

Leerlauftransaktionen eliminieren

Wenn Sie PGBouncer zum Skalieren verwenden möchten, müssen Sie unbedingt vermeiden, dass Transaktionen offen bleiben. Dazu müssen Sie autocommiton aktivieren. Dies ist mit SQLAlchemy nicht einfach ... es gibt drei Stellen, an denen etwas namens "Autocommit" festgelegt werden kann:

psycopg2 autocommit

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

Vermutlich unsicher, weil SQLAlchemy wissen muss, was darunter passiert.

Automatisches Festschreiben der Sitzung

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

Dies erfordert eine sorgfältige und explizite Übergabe:

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

Funktionsaufruf und Ausnahmebehandlung sind äußerst schwierig, da begin() und commit() nicht verschachtelt werden können:

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

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

In diesem Modus scheint psycopg2 autocommitFalse zu sein (Standardeinstellung)

Motor-Autocommit

Wenn Sie den Engine-Isolationsmodus beim Erstellen der Engine auf "AUTOCOMMIT" Setzen, wird ein neues Standardverhalten festgelegt, für das möglicherweise keine Änderungen am vorhandenen Code erforderlich sind.

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

In diesem Modus scheint psycopg2 autocommitTrue zu sein

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

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