Управление сеансами SQLAlchemy в длительном процессе - PullRequest
3 голосов
/ 14 сентября 2009

Сценарий:

  • На сервере приложений .NET ( Wonderware IAS / System Platform ) размещаются объекты автоматизации, которые обмениваются данными с различным оборудованием на заводском уровне.
  • CPython размещен на этом сервере приложений (используется Python для .NET ).
  • Объекты автоматизации имеют встроенные функции сценариев (используя пользовательский язык на основе .NET). Эти скрипты вызывают функции Python.

Функции Python являются частью системы для отслеживания незавершенного производства на заводе. Целью системы является отслеживание созданных виджетов вдоль процесса, обеспечение того, чтобы виджеты проходили процесс в правильном порядке, и проверка соблюдения определенных условий в процессе. История создания виджетов и состояние виджетов хранятся в реляционной базе данных, здесь 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())

Я хочу исключить управление сессиями из основных функций. По двум причинам:

  1. Существует еще одно семейство функций, функций последовательности. Функции последовательности вызывают несколько основных функций. Одна функция последовательности должна соответствовать одной транзакции базы данных.
  2. Мне нужно иметь возможность использовать библиотеку из других сред. а) Из веб-приложения TurboGears. В этом случае управление сеансом осуществляется TurboGears. б) из оболочки IPython. В этом случае commit / rollback будет явным.

(Мне очень жаль за длинный вопрос. Но я чувствовал, что мне нужно объяснить сценарий. Возможно, нет необходимости?)

Ответы [ 2 ]

4 голосов
/ 14 сентября 2009

Описанный декоратор подходит для долго работающих приложений, но вы можете столкнуться с проблемами, если случайно разделите объекты между запросами. Чтобы ошибки появлялись раньше и ничего не портили, лучше отменить сеанс с помощью 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 голос
/ 16 июня 2010

Попросите администратора WonderWare предоставить вам доступ к Wonderware Historian, вы можете довольно легко отслеживать значения тегов с помощью вызовов MSSQL через sqlalchemy, которые вы можете опрашивать время от времени.

Другим вариантом является использование набора инструментов archestra для прослушивания обновлений внутренних тегов и развертывания сервера в качестве платформы в галактике, с которой вы можете слушать.

...