2013-09-27 13 views
11

Używam Flask-SQLAlchemy 1.0, Flask 0.10, SQLAlchemy 0.8.2 i Python 2.7.5. Łączę się z MySQL 5.6 za pomocą Oracle MySQL Connector/Python 1.0.12.OperationalError: połączenie MySQL nie jest dostępne

Po ponownym uruchomieniu serwera WWW (albo wbudowany Apache2 lub Flask) otrzymuję wyjątek OperationalError: MySQL Connection not available po wygaśnięciu wait_timeout MySQL (domyślnie 8 godzin).

Znalazłem osoby z numerem similar problems i jawnie ustawiono SQLALCHEMY_POOL_RECYCLE = 7200, mimo że jest to Flask-SQLAlchemy's default. Po umieszczeniu punktu przerwania here widzę, że funkcja rozłączania pomyślnie wywołuje session.remove() po każdym żądaniu. Jakieś pomysły?

Aktualizacja 21.07.2014:

Ponieważ kwestia ta nadal otrzymuje uwagę, muszę dodać, że niewypróbować niektóre z propozycji. Dwóch moich prób wyglądało następujący:

First:

@contextmanager 
def safe_commit(): 
    try: 
     yield 
     db.session.commit() 
    except: 
     db.session.rollback() 
     raise 

To pozwoliło mi owinąć popełniają połączenia tak:

with safe_commit(): 
    model = Model(prop=value) 
    db.session.add(model) 

Jestem w 99% pewien, że to zrobiłem nie przegap żadnych połączeń db.session.commit za pomocą tej metody i nadal miałem problemy.

drugie:

def managed_session(): 
    def decorator(f): 
     @wraps(f) 
     def decorated_function(*args, **kwargs): 
      try: 
       response = f(*args, **kwargs) 
       db.session.commit() 
       return response 
      except: 
       db.session.rollback() 
       raise 
      finally: 
       db.session.close() 
     return decorated_function 
    return decorator 

Aby zapewnić, że nie brakowało każdy popełnić połączeń, zrobiłem kolby owijkę, która umożliwiła kod taki jak (jeśli dobrze pamiętam):

@managed_session() 
def hello(self): 
    model = Model(prop=value) 
    db.session.add(model) 

    return render_template(... 

Niestety, żadna z metod nie zadziałała. Przypominam też, że próbuję wywołać wywołania SELECT (1), próbując ponownie nawiązać połączenie, ale nie mam już tego kodu.

Dla mnie podstawą jest MySQL/SQL Alchemy ma problemy. Po migracji do Postgres nie musiałem się martwić o moje zatwierdzenia. Wszystko po prostu działało.

+0

Skończyłem grać z kwotą recyklingu, aż przestało się dziać. Będę musiał sprawdzić dokładną kwotę, ale uważam, że było to około 3200. – AlexLordThorsen

+0

Powinieneś również sprawdzić swoją aktualną konfigurację MySQL, możesz zapytać, aby uzyskać aktualny czas recyklingu. Miałem również ten problem i naprawiłem go, ustawiając limit czasu na 3600, jeśli to ci pomaga. Podjęcie rzeczywistej skonfigurowanej wartości wciąż stwarzało problemy, więc wziąłem połowę z tego i od tego czasu nie mam żadnych problemów. – javex

+0

@javax: Zmieniłeś 'wait_timeout' lub SqlAlchemy's pool_recycle MySQL? Tak czy inaczej, po prostu dostosowałem pool_recycle do 3600. Sprawdzę, co stanie się jutro rano. – Pakman

Odpowiedz

0

sqlalchemy oferuje 2 sposoby postępowania z rozłączenia, szczegóły w documentation

wersja skrócona:

  • Optymistycznie

użytku try...except blok do połowu wyjątki rozłączenia. Spowoduje to zwrócenie 500 w przypadku żądania błędu, a następnie aplikacja internetowa będzie działać normalnie. Więc używaj tego, jeśli rozłączenie zdarza się nieczęsto. Uwaga: musisz zawinąć każdą operację potencjalnego błędu w bloku try...except.

  • pesymistycznie (jeden używam)

Zasadniczo zrobić dodatkowy ping operację (coś jak SELECT 1) za każdym razem połączenie jest wyrejestrowany z basenu. Jeśli ping nie powiedzie się podnieść DisconnectionError, na którym pula host będzie próbował wymusić utworzenie nowego połączenia (w rzeczywistości pula spróbuje trzy razy, zanim oficjalnie się poddadzą). W ten sposób twoja aplikacja nie zobaczy błędu 500. Kompromisem jest wykonanie dodatkowego SQL, chociaż zgodnie z dokumentem koszty ogólne są niewielkie.

+0

Nie sądzę, że próbowałem podnieść "DisconnectionError". Jeśli znajdę trochę wolnego czasu, zrobię to i zaktualizuję wyniki. Dzięki za komentarz. – Pakman

4

Miałem ten problem i doprowadzało mnie to do szału. Próbowałem grać z SQLALCHEMY_POOL_RECYCLE, ale to nie rozwiązało problemu.

W końcu znalazłem http://docs.sqlalchemy.org/en/latest/orm/session.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it i zaadaptowałem do kolby-sqlalchemy.

Po rozpoczęciu korzystania z poniższego wzoru, nie widziałem problemu. Klucz wydaje się zawsze zapewniać, że zostanie wykonany commit() lub rollback(). Jeśli więc istnieje opcja if-then-else, która nie przepływa przez commit() (np. W przypadku wykrytego błędu), wykonaj także commit() lub rollback() przed przekierowaniem, przerwaniem, render_template.

class DoSomething(MethodView): 
    def get(self): 
     try: 
      # do stuff 
      db.session.commit() 
      return flask.render_template('sometemplate.html') 
     except: 
      db.session.rollback() 
      raise 
app.add_url_rule('/someurl',view_func=DoSomething.as_view('dosomething'),methods=['GET']) 

UPDATE 7/22/2014

odkryłem, że miałem też zmienić SQLALCHEMY_POOL_RECYCLE być mniejsza niż interactive_timeout MySQL. Na serwerze GoDaddy wartość parametru interactive_timeout ustawiono na 60, więc ustawię SQLALCHEMY_POOL_RECYCLE na 50. Myślę, że zarówno wzorzec użyty, jak i ten limit czasu były konieczne, aby problem zniknął, ale w tym momencie nie jestem pozytywny. Jednak jestem całkiem pewien, że gdy SQLALCHCHYY_POOL_RECYCLE był większy niż interactive_timeout, wciąż otrzymywałem błąd operacyjny.

+0

Dzięki za sugestię, ale spróbowałem tego w przeszłości bez powodzenia (zobacz moją aktualizację). – Pakman

+0

Tak, widziałem twoją migrację do PostgreSQL kiedy badałem mój problem, ale zdecydowałem się pozostać z MySQL. Nie widziałem tego problemu odkąd używam tego wzoru, podczas gdy zdarzało się to dość często. Przyznaję, że nie stworzyłem swoich własnych dekoratorów i wygląda na to, że w głównej linii twoje rozwiązanie będzie działało jak moje. Jedno pytanie jednak: W jaki sposób twoje rozwiązanie powoduje zatwierdzenie lub wycofanie poprzez przekierowanie lub przerwanie połączenia? Czy nie masz żadnego z nich w swoim kodzie? –

+0

Nie mam żadnych przekierowań/przerwań, ale nie rozumiem, dlaczego miałyby one znaczenie. Nie wpływają one na moje pierwsze rozwiązanie, aw mojej drugiej, 'response = f (* args, ** kwargs)' zostanie ustawione na odpowiedź przekierowania, a wyjątek abortu zostanie przechwycony i spowoduje wycofanie. – Pakman

0

natknąłem tej samej kwestii niedawno - pierwszy wniosek do bazy danych MySQL po długim okresie kolby & sqlalchemy bezczynności aplikacji (co najmniej 8 godzin) powoduje nieobsługiwany wyjątek, co z kolei pociąga za sobą 500 Internal Server Error : Połączenie niedostępne. Wszystkie kolejne prośby są w porządku.

udało mi się sprowadzić problem do połączenia MySQL poprzez zmniejszenie @@ session.wait_timeout (i @@ wszelki wypadek globalne) wartości do 5 sekund. Wtedy każda dziwna prośba była w porządku, a każda sekunda po 5-sekundowej pauzie nie powiodła się. Wniosek był oczywisty - SQLAlchemy korzystał z otwartego, ale przekroczonego limitu czasu na końcu połączenia z bazą danych.

Rozwiązanie

W moim przypadku okazało się rozwiązanie jest opisana w poście SQLAlchemy – MYSQL has gone away blogu:

The first thing to make sure of is [...] the value of pool_recycle should be less than your MYSQLs wait_timeout value.

W dokumentacji MySQL można znaleźć wait_timeout domyślne do 8 godzin (28 800 sekund), podczas gdy domyślną wartością domyślną dla silnika SQLAlchemy jest -1, co oznacza, że ​​nie jest w żaden sposób zawracany żaden z połączeń.Po prostu przekazałem wartość 21 600 (6 godzin) do funkcji create_engine i błąd zniknął.

Powiązane problemy