Wie schließe ich eine SQLAlchemy-Sitzung?

71

Nach dem, was wir in So schließen Sie die SQLalchemy-Verbindung in MySQL kommentiert haben , überprüfe ich die Verbindungen, die SQLAlchemy in meiner Datenbank erstellt, und kann sie nicht schließen, ohne Python zu beenden .

Wenn ich diesen Code in einer Python-Konsole ausführe, bleibt die Sitzung geöffnet, bis ich Python verlasse:

from sqlalchemy.orm import sessionmaker
from models import OneTable, get_engine

engine = get_engine(database="mydb")
session = sessionmaker(bind=engine)()

results = session.query(OneTable.company_name).all()

# some work with the data #

session.close()

und die einzige Problemumgehung, die ich gefunden habe, um es zu schließen, ist, engine.dispose()am Ende anzurufen .

Gemäß den Kommentaren in dem Link, den ich oben gegeben habe, sind meine Fragen jetzt:

  • Warum ist es engine.dispose()notwendig, Sitzungen zu schließen?
  • Reicht nicht session.close()aus?
fedorqui 'SO hör auf zu schaden'
quelle
1
Arbeiten mit sqlalchemy.pool.NullPoollöst Ihr Problem nicht?
Alvaro Fuentes
wie man AccessShareLockPostgres-Beziehungen löscht, nachdem die selectAbfrage ausgeführt wurde
Neuling

Antworten:

87

Es gibt hier eine zentrale Verwirrung über das Wort "Sitzung". Ich bin mir hier nicht sicher, aber es scheint, als würden Sie die SQLAlchemy-Sitzung mit einer MySQL @@ -Sitzung verwechseln. Dies bezieht sich auf den Umfang, in dem Sie zum ersten Mal eine Verbindung zu MySQL herstellen und die Verbindung trennen.

Diese beiden Konzepte sind nicht gleich . Eine SQLAlchemy-Sitzung repräsentiert im Allgemeinen den Umfang einer oder mehrerer Transaktionen bei einer bestimmten Datenbankverbindung.

Daher lautet die Antwort auf Ihre Frage im wahrsten Sinne des Wortes: session.close()"Wie wird eine SQLAlchemy-Sitzung ordnungsgemäß geschlossen?".

Der Rest Ihrer Frage weist jedoch darauf hin, dass Sie einige Funktionen Sessionwünschen, bei denen beim Schließen einer bestimmten Person auch die eigentliche DBAPI-Verbindung geschlossen werden soll.

Dies bedeutet im Grunde, dass Sie das Verbindungspooling deaktivieren möchten . Wie andere Antworten bereits erwähnen, verwenden Sie einfach genug NullPool .

zzzeek
quelle
7
Das hat es komplett geschafft. Als Sie darauf hinwiesen, bemerkte ich, dass ich die SQLAlchemy- und MySQL-Sitzungen mischte. Jetzt mit get_engine(database="mydb", poolclass=NullPool)ich bekomme es geschlossen, sobald ich session.close(). Vielen Dank!
Fedorqui 'SO hör auf zu schaden'
Ich glaube, ich habe ein Problem, weil die Sitzungen nicht ordnungsgemäß geschlossen wurden. Meine API gibt mir eine zufällige Anzahl von Zeilen. zB habe ich 10 Zeilen in meiner Tabelle (MySQL). Und wenn ich meine API drücke, erhalte ich 10, 0, 9 usw. Ergebnisse. PS Ich benutze sqlalchemy (nicht flock sqlalchemy). Bitte führen Sie mich.
Hussain
72

session.close() gibt die Verbindung zum Verbindungspool von Engine zurück und schließt die Verbindung nicht.

engine.dispose() schließt alle Verbindungen des Verbindungspools.

Die Engine verwendet keinen Verbindungspool, wenn Sie festlegen poolclass=NullPool. Die Verbindung (SQLAlchemy-Sitzung) wird also direkt danach geschlossen session.close().

julivico
quelle
4
Wird .disposefür alle anstehenden Commits warten?
Matanster
4
@matanster nein, laut Dokumentation: "Ausgecheckte Verbindungen werden nicht verworfen, wenn der Motor entsorgt oder Müll gesammelt wird". So dass alle nicht geschlossene Sessions effektiv durch ignoriert werdenengine.dispose()
Jamie Scott
0

In der LogicBank hatte ich eine Reihe von Tests. Bei jedem Test wurde vor dem Ausführen eine SQLite-Datenbank wie folgt kopiert:

copyfile(src=nw_source, dst=nw_loc)

Jeder Test wurde einzeln ausgeführt, schlug jedoch im discoverModus fehl . Es stellte sich heraus, dass die Datenbankkopie irgendwie nicht stattfand.

Es schien, dass vielleicht unittest nicht seriell ausgeführt wurden. Nicht so - Unittests laufen tatsächlich seriell ab. Das war also nicht das Problem (das protokollieren, um vielleicht jemandem etwas Zeit zu sparen).

Nach einer bemerkenswerten Menge an Thrashing scheint dies daran zu liegen, dass die Datenbank vom vorherigen Test nicht vollständig geschlossen wurde. Irgendwie störte das die obige Kopie. Meins ist nicht zu wundern warum ...

Dank der obigen Beiträge habe ich es folgendermaßen gelöst:

def tearDown(file: str, started_at: str, engine: sqlalchemy.engine.base.Engine, session: sqlalchemy.orm.session.Session):
"""
close session & engine, banner

:param file: caller, usually __file__
:param started_at: eg, str(datetime.now())
:param engine: eg, nw.logic import session, engine
:param session: from nw.logic import session, engine
:return:
"""

session.close()
engine.dispose(). # NOTE: close required before dispose!

print("\n")
print("**********************")
print("** Test complete, SQLAlchemy session/engine closed for: " + file)
print("** Started: " + started_at + " Ended: " + str(datetime.now()))
print("**********************")
val
quelle