SQLAlchemy v1.0.6
cx_Oracle v5.2
Abbiamo avuto un problema sul nostro codice di produzione per un po 'e infine lo abbiamo ridotto ai dati provenienti da SQLAlchemy.SQLAlchemy occasionalmente restituisce erroneamente un risultato vuoto
L'esecuzione della stessa query più volte a volte restituisce un risultato vuoto. In alcune condizioni, è possibile ottenere che restituisca un risultato vuoto ogni volta che il codice viene eseguito. Ciò nonostante il fatto che i dati nel database non siano stati modificati e che le versioni puramente SQL della stessa query eseguite direttamente su cx_Oracle restituiscano sempre il risultato corretto.
Ecco il codice dichiarativo per 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
E qui è il codice di prova:
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
La prima funzione, sqlalchemy_test
, avrà errore circa il 50% del tempo. La seconda funzione, cx_oracle_test
, non è ancora stata errata. Ora ecco cosa è interessante: il problema scompare se introduciamo una pausa per diversi secondi tra cursor = session.query(db.lot)
e results = cursor.first()
. Quindi sembra una sorta di problema di temporizzazione.
Qualsiasi indizio cosa sta succedendo qui?
MODIFICA: Ho semplificato il codice necessario per creare l'errore. Eccolo:
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\11.1.0\client_1\network\admin'
os.environ['PATH'] = r'C:\product\11.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
Ho avuto un problema anni fa con alcuni driver mysql che causavano problemi strani, apparentemente non deterministici come questo. Non posso dire per quanto riguarda Oracle, ma potresti voler provare un driver di database diverso per vedere se questo aiuta? –
Grazie per il suggerimento, Ned! – Vijchti
Altri test dimostrano che ciò accade solo in SQLAlchemy. Non cx_Oracle da solo, nemmeno quando si copia l'SQL generato da SQLAlchemy, non in un sistema diverso che utilizza un JDBC per connettersi al database - niente crea questo problema eccetto SQLAlchemy. La cosa più strana ... – Vijchti