2014-09-10 16 views
5

Mam dziwne błędy, które wydają się być spowodowane przez połączenia używane przez Sqlalchemy, których nie mogę dokładnie określić .. miałem nadzieję, że ktoś ma pojęcia, co się tutaj dzieje.Błędy połączenia SQLAlchemy

Pracujemy nad piramidą (wersja 1.5b1) i używamy Sqlalchemy (wersja 0.9.6) do wszystkich naszych połączeń z bazą danych. Czasami pojawiają się błędy związane z połączeniem dB lub sesji, większość czasu to byłby błąd cursor already closed lub This Connection is closed, ale mamy inne podobne wyjątki też:

(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 

Nie ma panaceum na ich powielanie, tylko odświeżając wiele razy, muszą się w pewnym momencie wydarzyć. Zrobiłem więc scenariusz z użyciem wielu mechanizmów do jednoczesnego rozsyłania różnych adresów URL i sprawdzania, gdzie i kiedy to się dzieje.

Wygląda na to, że wyzwolony adres URL nie ma większego znaczenia, błędy występują, gdy istnieją równoczesne żądania, które obejmują dłuższy czas (i inne żądania są wysyłane między nimi). Wydaje się to wskazywać na pewien problem z gwintowaniem; że sesja lub połączenie są dzielone między różne wątki.

Po googlowania dla tych zagadnień znalazłem wiele tematów, większość z nich powiedzieć używać scoped sesje, ale jest to, że używają ich już:

db_session = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), autocommit=False, autoflush=False)) 
db_meta = MetaData() 
  • Mamy BaseModel dla Wszystkie nasze obiekty orm:

    BaseModel = declarative_base (CLS = BaseModelObj, metaklasa = BaseMeta metadane = db_meta)

  • używamy animację pyramid_tm obsłużyć transakcje podczas żądania

  • Podnosimy db_session.remove() do piramidy NewResponse event (która jest uruchamiana po uruchomieniu wszystkiego). Próbowałem też umieścić go w osobnej animacji po pyramid_tm lub nawet nie robić tego w ogóle, żaden z nich nie wydaje się działać, więc wydarzenie odpowiedzi wydawało się być najbardziej czystym miejscem do umieszczenia go.

  • Tworzymy silnik w naszym głównym punkcie wyjścia dla naszego projektu piramidy i używamy NullPool i zostawiamy pulowanie połączeń dla pgbouncer. Mamy również skonfigurować sesję i wiązania dla naszych BaseModel tutaj: ('SQLAlchemy' config.registry.settings,, poolclass = NullPool)

    silnika = engine_from_config db_session.configure (wiążą = silnik, query_cls = FilterQuery) BaseModel.metadata.bind = silnik config.add_subscriber (cleanup_db_session, NewResponse) config.make_wsgi_app zwrotny()

  • naszym aplikacji mamy dostęp do wszystkich operacji db używany:

    z project.db importu db_session . .. db_sessi on.query (MyModel) .filter (...) db_session.execute (...)

  • Używamy psycopg2 == 2.5.2 obsługiwać połączenia z PostgreSQL z pgbouncer w między

  • Zrobiłem pewien brak odniesienia do db_session lub połączenia są zapisywane w dowolnym miejscu (co może prowadzić do innych wątków ich użyciem)

Próbowałem też spam przetestuj używając różnych serwerów sieciowych, używając kelnerki i cogenu, bardzo łatwo dostaję błędy, używając wsgiref nie mamy żadnych niespodzianek (które jest singlethreaded). Za pomocą uwsgi i gunicorn (4 pracowników, gevent) nie dostałem żadnych błędów.

Biorąc pod uwagę różnice w używanym serwerze internetowym, myślałem, że ma to związek z niektórymi serwerami obsługującymi żądania w wątkach, a niektóre z wykorzystaniem nowych procesów (może problem rozwidlenia)? Aby jeszcze bardziej komplikować sprawy, kiedy upłynęło trochę czasu i zrobiłem kilka nowych testów, problem zniknął w kelnerce, ale teraz stało się z gunicornem (gdy używam gevent)! Nie mam pojęcia, jak rozpocząć debugowanie tego ...

Na koniec, aby sprawdzić, co dzieje się z połączeniem, podłączyłem atrybut do połączenia na początku wykonywania kursora i próbowałem odczytać atrybut na koniec realizacji:

@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 

Niespodziewanie to czasem podniesiona wyjątek: „Connection” obiekt nie ma atrybutu „pdtb_start_timer”

które wydało mi się bardzo dziwne .. Znalazłem jedną dyskusję o czymś podobnym: https://groups.google.com/d/msg/sqlalchemy/GQZSjHAGkWM/rDflJvuyWnEJ Próbowałem dodać strategię = 'threadlocal' do silnika, który fr om, co rozumiem, powinno wymuszać 1 połączenie dla bieżnika. Ale nie miało to żadnego wpływu na błędy w widzeniu ... (oprócz niektórych niepowodzeń niepoprawnych, ponieważ potrzebuję dwóch różnych sesji/połączeń dla niektórych testów, a to zmusza jedno połączenie do skojarzenia)

Czy ktoś ma jakiś pomysł może się tu udać, lub mieć więcej wskazówek, jak zaatakować ten problem?

Z góry dziękuję!

Matthijs Blaas

+0

Mam aktualizację w tej kwestii, teraz konsekwentnie otrzymuję "(InterfaceError) kursor już zamknięty" wyjątki * tylko * przy użyciu gunicorn w trybie gevent: https://groups.google.com/d/msg/sqlalchemy/7szX4cbw2Ho/qBHcxYAfDgcJ –

Odpowiedz

1

Aktualizacja: Błędy gdzie spowodowane przez wiele poleceń, które wysyłają, gdzie w jednym przygotowanej instrukcji SQL. Wydaje się, że Psycopg2 na to pozwala, ale najwyraźniej może powodować dziwne problemy. Złącze PG8000 jest bardziej rygorystyczne i wycofywane z wielu poleceń, wysyłając jedno polecenie, które naprawiło problem!

Powiązane problemy