Datenbankverbindungen und WSGI

Django, Flask, Bottle, WSGI, CGI…
Antworten
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

Hallo,

ich frage mich gerade, wie ich am besten meine Datenbankverbindungen in Bottle herstellen soll. Ich will eine Verbindung zu einer Postgresql Datenbank herstellen und dazu psycopg nutzen, die WSGI app soll Bottle sein. Das ganze läuft unter Apache mpm/worker. Mir stellt sich hier die Frage, was am besten hinsichtlich des Verbindungsaufbaus und -wiederverwertung ist.

Folgende Dinge habe ich herausgefunden:
  • Das Bottle-Tutorial baut wohl in jeder Route eine neue Verbindung auf, hinsichtlich der Wiederverwendung der (teuren) Verbindung erscheint mir das erst einmal nicht sonderlich optimal.
  • Bottles SQLite Plugin ist da wohl etwas eleganter. Es wird ein Connection Objekt einmalig erzeugt und automatisch in die Route eingefügt.
  • Es gibt natürlich auch noch SqlAlchemy. Aber eigentlich möchte ich kein ORM nutzen, und es nur wegen des Connection Poolings zu installieren und wegen des vorhandenen Plugins für Bottle erscheint mir auch etwas Overkill für meine Zwecke, auch wenn es vergleichbar einfach wäre.
Was kann ich mir noch vorstellen? Im Modul global ein Connection Objekt erstellen und von innerhalb der Routen mir einen Cursor liefern lassen. Die Dokumentation meint auch, das wäre machbar, da threadsafe.

Code: Alles auswählen

import bottle
import psycopg2

app = bottle.Bottle()
conn = psycopg2.connect("dbname=test user=postgres")

@app.route('/')
def index():
    cur = conn.cursor()
    cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
    # usw.
Was gibt's denn noch? Gerade was die Verwendung von Datenbankverbindungen im WSGI / Threaded / Web Umfeld angeht, bin ich da etwas unsicher, wie das am besten zu händeln ( :mrgreen: ) ist.
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Du willst zumindest eine Connection pro Thread; dass connection objekte threadsafe sind heißt nicht dass du da mal eben 2 Cursor in verschiedenen Threads/requests verwenden darfst. Guck dir doch mal die Methoden auf der Connection an: Unter anderem commit und rollback; sprich du teilst dir dann mit anderen threads/requests eine Transaktion, nicht sehr gut :)

Der Weg vom Bottle tutorial ist schon mal relativ sinnvoll; allerdings wird man das im Normalfall "automagisch" machen, denn das will man nicht in jedem View wiederholen. System wie Django haben zb pro Thread eine Connection die dann nach request ende offen gehalten wird und der nächste Request braucht somit keine neue Verbidung aufbauen.
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

apollo13 hat geschrieben:Du willst zumindest eine Connection pro Thread; dass connection objekte threadsafe sind heißt nicht dass du da mal eben 2 Cursor in verschiedenen Threads/requests verwenden darfst. Guck dir doch mal die Methoden auf der Connection an: Unter anderem commit und rollback; sprich du teilst dir dann mit anderen threads/requests eine Transaktion, nicht sehr gut :)
Ah, ja. Das erscheint mir in dem Blickwinkel auch als keine allzu gute Idee. Aber das könnte man ja einem Connection Pool überlassen. Bei Beginn des Threads holt man sich die Connection und gibt sie am Ende wieder zurück ...
apollo13 hat geschrieben: Der Weg vom Bottle tutorial ist schon mal relativ sinnvoll; allerdings wird man das im Normalfall "automagisch" machen, denn das will man nicht in jedem View wiederholen. System wie Django haben zb pro Thread eine Connection die dann nach request ende offen gehalten wird und der nächste Request braucht somit keine neue Verbidung aufbauen.
Das ist aber auch irgendwie uncool. Wenn ich meine Anwendung unter WSGI mit 5 Prozessen und 25 Threads konfiguriere, kommen da aber schon eine ganze Menge Verbindungen zusammen, oder?

Was mir noch nicht ganz klar ist, inwieweit Instanzen von Klassen wie z.B. so ein Connectionpool Gültigkeit haben im WSGI/Bottle Umfeld. Wenn ich mir so einen Pool im Kopf meines Moduls instanziere, wo gilt der dann? Threadlokal, Prozessglobal oder ein Pool über alle Threads/Prozesse?

Sowas hier z.B.

Code: Alles auswählen

import bottle
import psycopg2

pool = psycopg2.pool.ThreadedConnectionPool(1, 5, pg, cursor_factory=DictCursor)
app = bottle.Bottle()

@app.route('/')
def index():
    with pool.getconn() as conn:
        with conn.cursor() as cur:
            # SQL
    pool.putconn(conn)
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

frabron hat geschrieben: Das ist aber auch irgendwie uncool. Wenn ich meine Anwendung unter WSGI mit 5 Prozessen und 25 Threads konfiguriere, kommen da aber schon eine ganze Menge Verbindungen zusammen, oder?
Also mehr als 2 Threads pro Prozess machen nicht allzuviel Sinn, aber ja, du musst deine Prozesse und Threads an deine Datenbank und System anpassen.
Was mir noch nicht ganz klar ist, inwieweit Instanzen von Klassen wie z.B. so ein Connectionpool Gültigkeit haben im WSGI/Bottle Umfeld. Wenn ich mir so einen Pool im Kopf meines Moduls instanziere, wo gilt der dann? Threadlokal, Prozessglobal oder ein Pool über alle Threads/Prozesse?
Module werden einmal pro Prozess initialisiert, ergo sollte der Prozessweit gelten, was der Name psycopg2.pool.ThreadedConnectionPool auch impliziert. pool.putconn sollte eigentlich nicht nötig sein wenn du getconn als context manager verwendest, würde ich aber in den docs nachlesen.
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

Mod_wsgi erzeugt standardmäßig 15 Threads pro Prozess wenn man nichts anderes einstellt. Deshalb kann ich nichts dazu sagen, ob mehr als 2 Threads pro Prozess Sinn machen oder nicht. Dass der Pool pro Prozess gilt, ist auf jeden Fall gut zu wissen.

Der Kontextmanager von psycopg2 ist leider nicht besonders gut beschrieben, zumindest nicht im Zusammenhang mit Pools. Ich werde auf der Mailingliste mal ne Anfrage stellen und schauen, ob dabei neue Erkenntnisse herauskommen. Zumindest ist das Abschliessen einer Transaktion ja auch ungleich mit dem Zurückgeben einer Verbindung
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Ah gut, mit der Transaktion macht das natürlich mehr Sinn :) Dann wir das mit dem putconn schon passen.
Antworten