2016-06-15 16 views
0

Ich habe eine Flask REST API, die mit einem Gunicorn/Nginx-Stack ausgeführt wird. Für jeden Thread, auf dem die API ausgeführt wird, wird einmal eine globale SQLAlchemy-Sitzung eingerichtet. Ich habe einen Endpunkt/Test/eingerichtet, um die Komponententests für die API auszuführen. Ein Test eine POST-Anforderung macht, etwas zu der Datenbank hinzuzufügen, hat dann eine endlich: Klausel aufzuräumen:Flask SQLAlchemy Sitzungen nicht synchron

def test_something(): 
    try: 
     url = "http://myposturl" 
     data = {"content" : "test post"} 
     headers = {'content-type': 'application/json'} 
     result = requests.post(url, json=data, headers=headers).json() 
     validate(result, myschema) 
    finally: 
     db.sqlsession.query(MyTable).filter(MyTable.content == "test post").delete() 
     db.sqlsession.commit() 

Das Problem ist, dass der Thread, auf die die POST-Anforderung gemacht hat jetzt eine „Test-Post“ Objekt in seiner Sitzung, aber die Datenbank hat kein solches Objekt, weil der Thread, auf dem die Tests ausgeführt wurden, dieses Objekt aus der Datenbank löschte. Also, wenn ich eine GET-Anfrage an den Server mache, etwa 1 in 4 Mal (ich habe 4 Gunicorn-Arbeiter), bekomme ich das "Test-Post" -Objekt, und 3 in 4-mal nicht. Das liegt daran, dass die Threads jeweils ihr eigenes Sitzungsobjekt haben und dass sie nicht mehr synchron sind, aber ich weiß nicht, was ich dagegen tun soll.

Hier ist mein Setup für meine SQLAlchemy-Sitzung:

def connectSQLAlchemy(): 
    import sqlalchemy 
    import sqlalchemy.orm 
    engine = sqlalchemy.create_engine(connection_string(DBConfig.USER, DBConfig.PASSWORD, DBConfig.HOST, DBConfig.DB)) 
    session_factory = sqlalchemy.orm.sessionmaker(bind=engine) 
    Session = sqlalchemy.orm.scoped_session(session_factory) 
    return Session() 

# Create a global session for everyone 
sqlsession = connectSQLAlchemy() 

Antwort

1

Bitte verwenden Sie flask-sqlalchemy Wenn Sie flask verwenden, kümmert es sich um den Lebenszyklus der Sitzung für Sie.

Wenn Sie darauf bestehen, es selbst zu tun, ist das korrekte Muster, eine Sitzung für jede Anfrage zu erstellen, anstatt eine globale Sitzung zu haben. Sie sollten jedes Mal, wenn Sie brauchen eine Sitzung

Session = scoped_session(session_factory, scopefunc=flask._app_ctx_stack.__ident_func__) 
return Session 

statt

Session = scoped_session(session_factory) 
return Session() 

Und tun

session = Session() 

tun. Aufgrund der scoped_session und der scopefunc gibt dies Ihnen eine andere Sitzung in jeder Anfrage zurück, aber die gleiche Sitzung in der gleichen Anfrage.

+0

Ich habe getan, was Sie gesagt haben, und ich habe self.session = db.Session() in der Funktion __init__ für eine neue APIResource (Resource) -Klasse, aber ich habe das gleiche Problem ... Ich füge hinzu etwas an die Datenbank und lösche es in separaten Threads, aber der Adding-Thread merkt sich das Ding. – Scott

+0

@Scott Sie sagen, Sie haben mehrere Threads * innerhalb derselben Anfrage *? – univerio

+0

Nein. Nur die Gunicorn-Fäden. Ich denke, das Problem mit dieser Lösung ist, dass ich die Sitzung am Ende der Anfrage irgendwo abreißen muss. – Scott

0

Ich habe es herausgefunden. Was ich tat, war ein Setup und Teardown auf die Anfrage in meiner App __init__.py hinzuzufügen:

@app.before_request 
def startup_session(): 
    db.session = db.connectSQLAlchemy() 

@app.teardown_request 
def shutdown_session(exception=None): 
    db.session.close() 

noch die globale Session-Objekt in meinem db-Modul:

db.py: 

.... 
session = None 
.... 

Die scoped_session behandelt die verschiedenen Threads, denke ich ...

Bitte beraten, wenn dies eine schreckliche Möglichkeit ist, dies aus irgendeinem Grund zu tun. = c)

+0

'scoped_session' behandelt nicht mehrere Threads wie diesen. Sobald Sie mehrere Threads haben, werden Sie in Schwierigkeiten geraten. – univerio