Python SQLAlchemy + Threading静默失败

时间:2019-02-28 14:43:20

标签: python python-3.x sqlalchemy

我正在尝试构建服务器来采样流价格订阅源并使用SQLAlchemy更新postgres数据库。我正在使用映射类的线程实例,这似乎可以工作,但不稳定。

Stream类的1个或2个实例没有问题,但是说10个,线程随机且无提示地失败。每次失败之前,SQLAlchemy都会给出一条错误消息,因此这似乎是杀死线程的原因。流没有任何问题,它始终稳定。

我是否错过了SQLAlchemy设置?是否有更好的方式将多个实时订阅馈入SQL?

代码:

import time
import json
from threading import Thread, Lock
import sqlalchemy as db
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base

# Setup SQLAlchemy
engine = db.create_engine('postgresql://localhost:5432/Project', echo=False)
metadata = db.MetaData(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()

Base.metadata.create_all(engine)
session = Session()

#DB classes
#static data table
class StockMaster(Base):
    __tablename__ = 'stock_master'
    id = db.Column(db.Integer, primary_key=True)
    ticker = db.Column(db.String)
    stock_name = db.Column(db.String)

    @classmethod
    def find_by_ticker(cls,ticker):
        return session.query(StockMaster).filter(StockMaster.ticker==ticker).first()

#live data table
class StockLive(Base):
    __tablename__ = 'stock_live'
    id = db.Column(db.Integer, primary_key=True)
    quote = db.Column(db.Numeric)
    timestamp = db.Column(db.Numeric)
    ticker_id = db.Column(db.Integer, db.ForeignKey('stock_master.id'))

    ticker = relationship("StockMaster", foreign_keys=[ticker_id])

    def __init__(self, quote, ticker_id, timestamp):
        self.quote=quote
        self.ticker_id=ticker_id
        self.timestamp=timestamp

    def save_to_db(self):
        session.add(self)
        session.commit()

    @classmethod
    def find_by_ticker_id(cls,ticker_id):
        return session.query(StockLive).filter(StockLive.ticker_id==ticker_id).first()

    @classmethod
    def find_by_ticker(cls,ticker):
        ticker_id = StockMaster.find_by_ticker(ticker).id
        return session.query(StockLive).filter(StockLive.ticker_id==ticker_id).first()


class Stream(Thread):
    def __init__(self,ticker):
        Thread.__init__(self)
        self.ticker=ticker
        self.quote=1
        self.data_set = StockLive.find_by_ticker(self.ticker)
        self.count=0

    def run(self):
        con.subscribe(self.ticker)
        current_mid=1
        while True:
            new_data = json.loads(con.get_price(self.ticker).to_json())
            new_mid = new_data['Mid']

            if new_mid == current_mid:
                pass
            else:
                current_mid = new_mid
                self.data_set.quote = current_mid
                self.data_set.timestamp = time.time()
                try:
                    self.data_set.save_to_db()
                    self.count+=1
                except:
                    self.data_set = StockLive.find_by_ticker(self.ticker)
                    print('error saving to db for '+self.ticker)
            time.sleep(.1)


if __name__ == '__main__':
    threads={}
    for ticker in tickerlist:
        try:
            threads[ticker]=Stream(ticker)
            threads[ticker].setName('Thread ' + ticker)
            threads[ticker].start()
        except:
            print('Error setting up '+ticker)

    while True:
        for ticker in tickerlist:
            if threads[ticker].isAlive()==False:
                threads[ticker]=Stream(ticker)

SQLAlchemy错误消息:

  

/anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py:2323:   SAWarning:当前未使用'Session.add()'操作   在刷新过程的执行阶段受支持。结果可能   不一致。考虑使用备用事件侦听器或   连接级操作。 % 方法)   /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py:2425:   SAWarning:属性历史事件累积在1个先前清除的事件上   内部刷新事件处理程序中的实例已被重置,并且将   不会导致数据库更新。考虑使用set_committed_value()   在内部冲洗事件处理程序中避免此警告。 %len_)   线程MSFT线程中的异常:追溯(最近一次调用):
  文件   “ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _flush中的第2436行       transaction.commit()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   第465行,正在提交       self._assert_active(prepared_ok = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _assert_active中的第285行       引发sa_exc.ResourceClosedError(closed_msg)sqlalchemy.exc.ResourceClosedError:此交易已关闭

     

在处理上述异常期间,发生了另一个异常:

     

回溯(最近通话最近):文件   “”,第48行,正在运行       self.data_set.save_to_db()在save_to_db中的文件““,第44行       session.commit()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   第954行,正在提交       self.transaction.commit()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   第467行,正在提交       self._prepare_impl()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _prepare_impl中的第447行       self.session.flush()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   2313行,齐平       self._flush(objects)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _flush中的第2440行       transaction.rollback(_capture_exception = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py”,   第76行,退出       compat.reraise(类型_,值,回溯)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/util/compat.py”,   第249行,加价       提高价值文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _flush中的第2440行       transaction.rollback(_capture_exception = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   回滚中的第483行       self._assert_active(prepared_ok = True,rollback_ok = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _assert_active中的第285行       引发sa_exc.ResourceClosedError(closed_msg)sqlalchemy.exc.ResourceClosedError:此交易已关闭

     

在处理上述异常期间,发生了另一个异常:

     

回溯(最近通话最近):文件   _bootstrap_inner中的“ /anaconda3/lib/python3.7/threading.py”,第917行       self.run()运行中的文件“”,第53行       self.data_set = StockLive.find_by_ticker(self.ccy)文件“”,第52行,在find_by_ticker中       ticker_id = StockMaster.find_by_ticker(ticker).id文件“”,在find_by_ticker的第23行       返回session.query(StockMaster).filter(StockMaster.ticker == ticker).first()   文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/query.py”,   首先是2895行       ret = list(self [0:1])文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/query.py”,行   2687,在获取中       返回列表文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/query.py”,行   2994,在 iter 中       self.session._autoflush()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _autoflush中的第1493行       self.flush()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   2313行,齐平       self._flush(objects)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _flush中的第2400行       subtransactions = True)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   865行,开始       nested = nested)文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _begin中的第297行       self._assert_active()文件“ /anaconda3/lib/python3.7/site-packages/sqlalchemy/orm/session.py”,   _assert_active中的第264行       “此会话处于“准备”状态;没有其他” sqlalchemy.exc.InvalidRequestError:此会话处于“准备”状态   州;在此事务内无法发出进一步的SQL。

1 个答案:

答案 0 :(得分:0)

您的代码可能还有其他问题,但是一个明显的问题是您与多个线程共享session

应该在每个线程中创建它们,而不是设置全局session

我无法运行您的代码,但是您可以尝试执行以下操作:

完全删除全局会话变量。您不需要它。然后修改您的方法和线程以包括本地会话:

@classmethod
def find_by_ticker(cls,ticker, session):
    return session.query(StockMaster).filter(StockMaster.ticker==ticker).first()

...

class Stream(Thread):
    def __init__(self,ticker):
        Thread.__init__(self)
        self.ticker=ticker
        self.quote=1
        self.session = Session()
        self.data_set = StockLive.find_by_ticker(self.ticker, self.session)
        self.count=0

或者类似的东西。现在,这将使每个线程都有自己的会话,并且您的代码将开始表现得更好。