SQLAlchemy 偶尔会错误地 returns 一个空结果
SQLAlchemy occasionally erroneously returns an empty result
SQLAlchemy v1.0.6
cx_Oracle v5.2
一段时间以来,我们的生产代码一直存在问题,最终将问题缩小到从 SQLAlchemy 返回的数据。
运行 多次相同的查询有时会 return 一个空结果。在某些情况下,我们可以在每次执行代码时得到一个空结果return。尽管事实上数据库中的数据根本没有改变,而且同一查询 运行 的纯 SQL 版本直接在 cx_Oracle 上总是 return 是正确的结果。
这是 SQLAlchemy 的声明代码:
class Database:
def __init__(self, service_name, database, username, password):
"""
service_name (str): The service name as defined in tnsnames.ora.
database (str): The database within the chosen service.
"""
self.engine = create_engine(
r'oracle+cx_oracle://{username}:{password}@{service_name}'.format(username=username, password=password,
service_name=service_name),
case_sensitive=False)
self.session_maker = sessionmaker(bind=self.engine, autoflush=False, autocommit=False)
# Database name must be injected into every table definition; this is why tables must be procedurally generated.
self.Base = declarative_base() # base class for all database tables
self.build_tables(database)
def make_session(self):
"""Create a read-only session for the database."""
def readonly_abort():
raise Exception('writing is prohibited; db is read-only')
session = self.session_maker()
session.flush = readonly_abort
return session
def build_tables(self, database):
class Lot(self.Base):
__tablename__ = 'lot'
__table_args__ = {'schema': database}
lot_key = Column(Integer, primary_key=True)
lot_id = Column(String, name='lot_id')
self.lot = Lot
这里是测试代码:
def sqlalchemy_test():
db = dp_orm.Database(service_name, database)
session = db.make_session()
cursor = session.query(db.lot)
results = cursor.first()
if results is None:
raise Exception
def cx_oracle_test():
import cx_Oracle
import set_environment_variables
conn = cx_Oracle.Connection(username, password, service_name)
cursor = conn.cursor()
c = cursor.execute('SELECT * FROM {}.lot WHERE rownum <= 1'.format(database))
results = list(c)
if len(results) != 1:
raise Exception
第一个函数 sqlalchemy_test
大约有 50% 的时间会出错。第二个函数cx_oracle_test
还没有出错。现在有趣的是:如果我们在 cursor = session.query(db.lot)
和 results = cursor.first()
之间引入几秒钟的停顿,问题就会消失。所以它看起来像是某种时间问题。
知道这里发生了什么吗?
编辑:
我已经简化了创建错误所需的代码。在这里:
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# Fix environment variables
import os
try:
del os.environ['ORACLE_HOME']
except KeyError:
pass
os.environ['TNS_ADMIN'] = r'C:\product.1.0\client_1\network\admin'
os.environ['PATH'] = r'C:\product.1.0\client_1\BIN;' + os.environ['PATH']
engine = create_engine(r'oracle+cx_oracle://{username}:{password}@{service_name}'.format(username='USER', password='PASSWORD', service_name='SERVICE'))
session_maker = sessionmaker(bind=engine)
base_class = declarative_base()
class Lot(base_class):
__tablename__ = 'lot'
__table_args__ = {'schema': 'SCHEMA_NAME'}
lot_key = Column(Integer, primary_key=True)
lot_id = Column(String)
session = session_maker()
cursor = session.query(Lot)
result = cursor.first()
if result is None:
raise Exception
找到答案:问题是cx_Oracle 5.2。重新安装 cx_Oracle 5.1.3 解决了问题。
SQLAlchemy v1.0.6
cx_Oracle v5.2
一段时间以来,我们的生产代码一直存在问题,最终将问题缩小到从 SQLAlchemy 返回的数据。
运行 多次相同的查询有时会 return 一个空结果。在某些情况下,我们可以在每次执行代码时得到一个空结果return。尽管事实上数据库中的数据根本没有改变,而且同一查询 运行 的纯 SQL 版本直接在 cx_Oracle 上总是 return 是正确的结果。
这是 SQLAlchemy 的声明代码:
class Database:
def __init__(self, service_name, database, username, password):
"""
service_name (str): The service name as defined in tnsnames.ora.
database (str): The database within the chosen service.
"""
self.engine = create_engine(
r'oracle+cx_oracle://{username}:{password}@{service_name}'.format(username=username, password=password,
service_name=service_name),
case_sensitive=False)
self.session_maker = sessionmaker(bind=self.engine, autoflush=False, autocommit=False)
# Database name must be injected into every table definition; this is why tables must be procedurally generated.
self.Base = declarative_base() # base class for all database tables
self.build_tables(database)
def make_session(self):
"""Create a read-only session for the database."""
def readonly_abort():
raise Exception('writing is prohibited; db is read-only')
session = self.session_maker()
session.flush = readonly_abort
return session
def build_tables(self, database):
class Lot(self.Base):
__tablename__ = 'lot'
__table_args__ = {'schema': database}
lot_key = Column(Integer, primary_key=True)
lot_id = Column(String, name='lot_id')
self.lot = Lot
这里是测试代码:
def sqlalchemy_test():
db = dp_orm.Database(service_name, database)
session = db.make_session()
cursor = session.query(db.lot)
results = cursor.first()
if results is None:
raise Exception
def cx_oracle_test():
import cx_Oracle
import set_environment_variables
conn = cx_Oracle.Connection(username, password, service_name)
cursor = conn.cursor()
c = cursor.execute('SELECT * FROM {}.lot WHERE rownum <= 1'.format(database))
results = list(c)
if len(results) != 1:
raise Exception
第一个函数 sqlalchemy_test
大约有 50% 的时间会出错。第二个函数cx_oracle_test
还没有出错。现在有趣的是:如果我们在 cursor = session.query(db.lot)
和 results = cursor.first()
之间引入几秒钟的停顿,问题就会消失。所以它看起来像是某种时间问题。
知道这里发生了什么吗?
编辑: 我已经简化了创建错误所需的代码。在这里:
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# Fix environment variables
import os
try:
del os.environ['ORACLE_HOME']
except KeyError:
pass
os.environ['TNS_ADMIN'] = r'C:\product.1.0\client_1\network\admin'
os.environ['PATH'] = r'C:\product.1.0\client_1\BIN;' + os.environ['PATH']
engine = create_engine(r'oracle+cx_oracle://{username}:{password}@{service_name}'.format(username='USER', password='PASSWORD', service_name='SERVICE'))
session_maker = sessionmaker(bind=engine)
base_class = declarative_base()
class Lot(base_class):
__tablename__ = 'lot'
__table_args__ = {'schema': 'SCHEMA_NAME'}
lot_key = Column(Integer, primary_key=True)
lot_id = Column(String)
session = session_maker()
cursor = session.query(Lot)
result = cursor.first()
if result is None:
raise Exception
找到答案:问题是cx_Oracle 5.2。重新安装 cx_Oracle 5.1.3 解决了问题。