情景:
Python功能是在工厂车间跟踪Work-In-Progress的系统的一部分。系统的目的是跟踪流程中生成的小部件,确保小部件以正确的顺序完成整个过程,并检查流程中是否满足某些条件。小部件生产历史和小部件状态存储在关系数据库中,这是SQLAlchemy发挥作用的地方。
例如,当窗口小部件通过扫描程序时,自动化软件会触发以下脚本(使用应用程序服务器的自定义脚本语言编写):
' 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;
该脚本调用WidgetScanned
python函数:
# 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
我的问题是:如何在这种情况下最好地管理SQLAlchemy会话?应用程序服务器是一个长时间运行的进程,通常在重新启动之间运行数月。应用程序服务器是单线程的。
目前,我采取以下方式:
我将装饰器应用于我对应用程序服务器可用的函数:
# 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)
问题:上面的装饰器是否适合在长时间运行的过程中处理会话?我应该拨打session.remove()
吗?
SQLAlchemy会话对象是作用域会话:
# pywip/database.py
from sqlalchemy.orm import scoped_session, sessionmaker
session = scoped_session(sessionmaker())
我希望将会话管理保留在基本功能之外。有两个原因:
(对于这个长期的问题,我真的很抱歉。但我觉得我需要解释这个场景。也许没有必要?)
答案 0 :(得分:4)
所描述的装饰器适用于长时间运行的应用程序,但如果您不小心在请求之间共享对象,则可能会遇到麻烦。为了使错误更早出现而不损坏任何内容,最好使用session.remove()丢弃会话。
try:
try:
retval = func(*args, **kwargs)
session.commit()
return retval
except:
session.rollback()
raise
finally:
session.remove()
或者,如果您可以使用with
上下文管理器:
try:
with session.registry().transaction:
return func(*args, **kwargs)
finally:
session.remove()
顺便说一下,您可能希望在查询中使用.with_lockmode('update')
,这样您的验证就不会在陈旧数据上运行。
答案 1 :(得分:1)
请求您的WonderWare管理员授予您访问Wonderware Historian的权限,您可以通过sqlalchemy上的MSSQL调用轻松跟踪标记的值,您可以经常轮询。
另一种选择是使用archestra工具包来监听内部标签更新,并将服务器部署为可以收听的星系中的平台。