SQLAlchemy Session-Management in lang laufenden Prozess
-
07-07-2019 - |
Frage
Szenario:
- Eine .NET-basierten Anwendungsserver ( Wonder IAS / System Platform ) beherbergt Automatisierungsobjekte, die mit verschiedenen Geräten in der Fabrik zu kommunizieren.
- CPython wird in diesem Anwendungsserver gehostet (unter Verwendung von Python für .NET ).
- Die Automatisierungsobjekte haben Scripting-Funktionalität eingebaut (eine benutzerdefinierte verwenden, .NET-basierte Sprache). Diese Skripte rufen Python-Funktionen.
Die Python-Funktionen sind Teil eines Systems, Work-in-Fortschritt in der Fabrik zu verfolgen. Der Zweck des Systems ist es, die erzeugten Widgets entlang des Prozesses zu verfolgen, stellen Sie sicher, dass die Widgets in der richtigen Reihenfolge durch den Prozess gehen, und prüfen Sie, dass bestimmte Bedingungen entlang des Prozesses erfüllt sind. Das Widget Produktionsgeschichte und Widget-Zustand in einer relationalen Datenbank gespeichert ist, das ist, wo SQLAlchemy seine Rolle spielt.
Wenn zum Beispiel eines Widget einen Scanner passiert, löst die Automatisierungssoftware die folgenden Skript (geschrieben in dem benutzerdefinierten Skriptsprache des Anwendungsservers):
' wiget_id and scanner_id provided by automation object
' ExecFunction() takes care of calling a CPython function
retval = ExecFunction("WidgetScanned", widget_id, scanner_id);
' if the python function raises an Exception, ErrorOccured will be true
' in this case, any errors should cause the production line to stop.
if (retval.ErrorOccured) then
ProductionLine.Running = False;
InformationBoard.DisplayText = "ERROR: " + retval.Exception.Message;
InformationBoard.SoundAlarm = True
end if;
Das Skript ruft die WidgetScanned
Python-Funktion:
# pywip/functions.py
from pywip.database import session
from pywip.model import Widget, WidgetHistoryItem
from pywip import validation, StatusMessage
from datetime import datetime
def WidgetScanned(widget_id, scanner_id):
widget = session.query(Widget).get(widget_id)
validation.validate_widget_passed_scanner(widget, scanner) # raises exception on error
widget.history.append(WidgetHistoryItem(timestamp=datetime.now(), action=u"SCANNED", scanner_id=scanner_id))
widget.last_scanner = scanner_id
widget.last_update = datetime.now()
return StatusMessage("OK")
# ... there are a dozen similar functions
Meine Frage ist: Wie am besten ich SQLAlchemy Sitzungen in diesem Szenario verwalten Der Anwendungsserver ist ein lang andauernde Prozess, in der Regel Monate zwischen Neustart ausgeführt wird. Der Anwendungsserver ist single-threaded.
Zur Zeit mache ich es folgendermaßen:
Ich beantrage einen Dekorateur auf die Funktionen, die ich auf dem Anwendungsserver avaliable machen:
# pywip/iasfunctions.py
from pywip import functions
def ias_session_handling(func):
def _ias_session_handling(*args, **kwargs):
try:
retval = func(*args, **kwargs)
session.commit()
return retval
except:
session.rollback()
raise
return _ias_session_handling
# ... actually I populate this module with decorated versions of all the functions in pywip.functions dynamically
WidgetScanned = ias_session_handling(functions.WidgetScanned)
Frage:? Ist der Dekorateur oben geeignet für Sitzungen in einem langlaufende Prozess der Handhabung Soll ich session.remove()
nennen
Das SQLAlchemy-Session-Objekt ist eine scoped Sitzung:
# pywip/database.py
from sqlalchemy.orm import scoped_session, sessionmaker
session = scoped_session(sessionmaker())
Ich möchte aus den grundlegenden Funktionen des Session-Management halten. Aus zwei Gründen:
- Es gibt eine andere Familie von Funktionen, Sequenzfunktionen. Die Sequenzfunktionen aufrufen mehrere der Grundfunktionen. Eine Sequenz-Funktion sollte eine Datenbanktransaktion entsprechen.
- Ich brauche die Bibliothek aus anderen Umgebungen verwenden zu können. a) Aus einer Turbogears Web-Anwendung. In diesem Fall wird die Sitzungsverwaltung von Turbogears getan. b) Von einer IPython Shell. In diesem Fall Commit / Rollback explizit sein wird.
(Ich bin wirklich leid für die lange Frage. Aber ich habe das Gefühl erforderlich, um das Szenario zu erklären. Vielleicht nicht notwendig?)
Lösung
Die beschriebene Dekorateur ist für lange laufende Anwendungen geeignet, aber Sie können in Schwierigkeiten geraten, wenn Sie versehentlich Objekte zwischen Anfragen teilen. Um die Fehler erscheinen früher und nicht korrupt etwas es besser ist, die Sitzung mit session.remove () zu verwerfen.
try:
try:
retval = func(*args, **kwargs)
session.commit()
return retval
except:
session.rollback()
raise
finally:
session.remove()
Oder wenn Sie den with
Kontext-Manager verwenden können:
try:
with session.registry().transaction:
return func(*args, **kwargs)
finally:
session.remove()
By the way, möchten Sie vielleicht .with_lockmode('update')
auf der Abfrage verwenden, um Ihre Validate läuft nicht auf veralteten Daten.
Andere Tipps
Fragen Sie Ihren Administrator Wonder Sie an den Historian Zugang zu geben, können Sie die Werte der Variablen verfolgen ziemlich leicht über MSSQL über sqlalchemy Anrufe, die Sie jeder so oft abfragen kann.
Eine andere Möglichkeit ist es, die ArchestrA-Toolkit zu verwenden für die interne Variable Updates zu hören und einen Server als Plattform in der Galaxie im Einsatz haben, die Sie von hören.