2015-07-13 15 views
5
SQLAlchemy v1.0.6 
cx_Oracle v5.2 

Przez jakiś czas mieliśmy problem z naszym kodem produkcyjnym i ostatecznie zawęziliśmy go do danych wracających z SQLAlchemy.SQLAlchemy od czasu do czasu błędnie zwraca pusty wynik

Wielokrotne uruchamianie tego samego zapytania czasami zwróci pusty wynik. W pewnych warunkach możemy uzyskać to, aby zwracać pusty wynik za każdym razem, gdy kod jest wykonywany. Dzieje się tak pomimo faktu, że dane w bazie danych w ogóle się nie zmieniły i że czyste wersje SQL tej samej kwerendy uruchomionej bezpośrednio na cx_Oracle zawsze zwracają poprawny wynik.

Oto kod deklaratywna dla 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 

A oto kod testowy:

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 

Pierwsza funkcja, sqlalchemy_test będzie błędów o 50% czasu. Druga funkcja, cx_oracle_test, nie została jeszcze usunięta. Oto co jest interesujące: problem znika, jeśli wprowadzimy przerwę na kilka sekund między cursor = session.query(db.lot) i results = cursor.first(). Wygląda więc na jakiś problem z timingiem.

Dowolna wskazówka, co się tutaj dzieje?

EDYCJA: Uprościliśmy kod niezbędny do utworzenia błędu. Oto ona:

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 
+0

Miałem problem lat temu z niektórymi sterownikami mysql, które powodowały dziwne, pozornie niedeterministyczne problemy, jak ten. Nie mogę powiedzieć w odniesieniu do Oracle, ale może chcesz wypróbować inny sterownik bazy danych, aby sprawdzić, czy to pomaga? –

+0

Dziękuję za sugestię, Ned! – Vijchti

+0

Więcej testów pokazuje, że dzieje się tak tylko w SQLAlchemy. Nie cx_Oracle sam, nawet podczas kopiowania SQL wygenerowanego przez SQLAlchemy, a nie w innym systemie, który używa JDBC do połączenia z bazą danych - nic nie tworzy tego problemu z wyjątkiem SQLAlchemy. Najdziwniejsze rzeczy ... – Vijchti

Odpowiedz

4

Odpowiedź znaleziona: problem polegał na cx_Oracle 5.2. Ponowna instalacja cx_Oracle 5.1.3 rozwiązała problem.

Powiązane problemy