2014-09-10 15 views
5

Sto sperimentando alcuni bug strani che sembrano essere causati dalle connessioni utilizzate da Sqlalchemy, che non riesco a definire esattamente .. speravo che qualcuno avesse un indizio che cosa sta succedendo qui.Errori di connessione SQLAlchemy

Stiamo lavorando su una Piramide (versione 1.5b1) e utilizziamo Sqlalchemy (versione 0.9.6) per tutta la nostra connettività di database. A volte otteniamo errori relativi alla connessione db o la sessione, la maggior parte delle volte questo sarebbe un errore di cursor already closed o This Connection is closed, ma otteniamo altre eccezioni relative anche:

(OperationalError) connection pointer is NULL 
(InterfaceError) cursor already closed 
Parent instance <...> is not bound to a Session, and no contextual session is established; lazy load operation of attribute '...' cannot proceed 

A conflicting state is already present in the identity map for key (<class '...'>, (1001L,)) 
This Connection is closed (original cause: ResourceClosedError: This Connection is closed) 
(InterfaceError) cursor already closed 
Parent instance <...> is not bound to a Session; lazy load operation of attribute '...' cannot proceed 
Parent instance <...> is not bound to a Session, and no contextual session is established; lazy load operation of attribute '...' cannot proceed 
'NoneType' object has no attribute 'twophase' 
(OperationalError) connection pointer is NULL 
This session is in 'prepared' state; no further 

Non c'è pallottola d'argento di riprodurli, solo rinfrescando molte volte sono obbligati ad accadere uno ad un certo punto. Così ho creato uno script usando la multi-meccanizzazione per inviare spam diversi URL contemporaneamente e vedere dove e quando accade.

Sembra che l'url attivato non contenga molto, gli errori si verificano quando ci sono richieste concorrenti che si estendono per un periodo più lungo (e altre richieste vengono servite in mezzo). Questo sembra indicare che esiste un qualche tipo di problema di threading; che la sessione o la connessione sia condivisa tra thread diversi.

Dopo googling per questi problemi che ho trovato un sacco di argomenti, la maggior parte di loro dicono di usare le sessioni di ambito, ma la cosa è che vengono utilizzati già:

db_session = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), autocommit=False, autoflush=False)) 
db_meta = MetaData() 
  • Abbiamo un BaseModel per tutti i nostri oggetti ORM:

    BaseModel = declarative_base (cls = BaseModelObj, metaclasse = BaseMeta, metadata = db_meta)

  • Usiamo l'interpolazione pyramid_tm da gestire transazioni durante la richiesta

  • Agganciamo db_session.remove() all'evento NewResponse della piramide (che viene attivato dopo che tutto è stato eseguito). Ho anche provato a metterlo in un'interpolazione separata in esecuzione dopo pyramid_tm o addirittura a non farlo affatto, nessuno di questi sembra avere effetto, quindi l'evento di risposta sembrava il posto più pulito per dirlo.

  • Creiamo il motore nel nostro principale punto di accesso del nostro progetto piramidale e usiamo un NullPool e lasciamo il pool di connessioni a pgbouncer. Abbiamo anche configurare la sessione e si legano per i nostri BaseModel qui: ('SQLAlchemy' config.registry.settings,, poolclass = NullPool)

    motore = engine_from_config db_session.configure (bind = motore, query_cls = FilterQuery) BaseModel.metadata.bind = motore config.add_subscriber (cleanup_db_session, NewResponse) ritorno config.make_wsgi_app()

  • Nella nostra applicazione abbiamo accedere a tutte le operazioni db utilizzando:

    da project.db importazione db_session . .. db_sessi on.query (MyModel) .filter (...) db_session.execute (...)

  • Utilizziamo psycopg2 == 2.5.2 per gestire la connessione a postgres con pgbouncer tra

  • Ho assicurato riferimenti di db_session o connessioni vengono salvate ovunque (che potrebbe causare altri thread riutilizzarli)

Ho provato anche lo spamming test usando diversi server web, usando la cameriera e il cogen ho ottenuto gli errori molto facilmente, usando wsgiref non abbiamo sorprendentemente errori (che è singlethreaded). Usando uwsgi e gunicorn (4 lavoratori, gevent) non ho avuto errori.

Date le differenze nel server web utilizzato, ho pensato che abbia a che fare con alcuni server Web che gestiscono richieste in thread e altri che usano nuovi processi (forse un problema di fork)? A complicare ulteriormente le cose, quando il tempo è passato e ho fatto alcuni nuovi test, il problema era sparito in cameriera ma ora è successo con gunicorn (quando si usa gevent)! Non ho idea di come eseguire il debug di questo ...

Infine, per testare cosa succede alla connessione, ho collegato un attributo alla connessione all'inizio del cursore esegui e ho provato a leggere l'attributo in uscita fine della esecuzione:

@event.listens_for(Engine, "before_cursor_execute") 
def _before_cursor_execute(conn, cursor, stmt, params, context, execmany): 
    conn.pdtb_start_timer = time.time() 

@event.listens_for(Engine, "after_cursor_execute") 
def _after_cursor_execute(conn, cursor, stmt, params, context, execmany): 
    print conn.pdtb_start_timer 

Sorprendentemente questo a volte ha sollevato un'eccezione: 'Connessione' oggetto non ha attributo 'pdtb_start_timer'

il che mi ha colpito come molto strano .. ho trovato uno discussione su qualcosa di simile: https://groups.google.com/d/msg/sqlalchemy/GQZSjHAGkWM/rDflJvuyWnEJ E provato ad aggiungere la strategia = 'threadlocal' al motore, che fr ma quello che ho capito dovrebbe forzare 1 connessione per il battistrada. Ma non ha avuto alcun effetto sugli errori che sto vedendo .. (a parte alcuni non riescono perché ho bisogno di due sessioni/connessioni diverse per alcuni test e questo obbliga ad associare 1 connessione)

Qualcuno ha qualche idea di cosa potrebbe andare avanti qui o avere altri suggerimenti su come attaccare questo problema?

Grazie in anticipo!

Matthijs Blaas

+0

Ho un aggiornamento sul problema, ora ottengo sempre "cursore (InterfaceError) già chiuso" eccezioni * solo * utilizzando gunicorn in modalità gevent: https://groups.google.com/d/msg/sqlalchemy/7szX4cbw2Ho/qBHcxYAfDgcJ –

risposta

1

Aggiornamento: Gli errori in cui causati da più comandi che invia in cui in una dichiarazione di sql preparata. Psycopg2 sembra consentirlo, ma a quanto pare può causare strani problemi. Il connettore PG8000 è più rigido e salvato su più comandi, l'invio di un comando ha risolto il problema!

Problemi correlati