Hatena::ブログ(Diary)

Memo

2016-02-20

[] SQLAlchemy との戦い

ここ数ヶ月 SQLAlchemy を使って開発をしている。


開発当初は特に問題もなく調子よく行っていたが、自分のローカル環境の MySQL の設定を本番に近づけたため、 SQLAlchemy がエラーをはいた - Memo が出た。

pool_recycle の値を短くすれば解決と思ったが、解決しなかった。

前提として FW は Flask を利用。SQLAlchemy は素で scoped_session を使っている。

Flask-SQLAlchey や Flask-Alchemy-Session は使ってない。

autocommit, autoflush は False の設定。


でたエラーが

sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2013, 'Lost connection to MySQL server during query') 

だったり

sqlalchemy.exc.StatementError: (sqlalchemy.exc.InvalidRequestError) Can't reconnect until invalid transaction is rolled back 

だったり。


現象としては、一つのページにとどまっていて、wait_timeout で設定した 60 秒を超えて、ページをリロード(MySQL にアクセス)した際にでる。

# 更新系は出ない


とりあえずぐぐって、出てきたページを片っ端から試してみた。

SQLAlchemyの OperationalError: MySQL Connection not available エラー - Life is Really Short, Have Your Life!!

コメント欄にあるように、session.remove() をリクエストの最後に呼んでやる。

これは元々 Flask の teardown_request で呼んでいる。

効果無し。


FlaskとSQLAlchemyを使っててMySQL server has gone awayってなる - petitviolet_blog

SQLAlchemy+MySQLで「Lost connection to MySQL server during query」|python|サムライファクトリー開発ブログ

にあるように、Pool event を使って、MySQL リクエストごとに ping を飛ばす。

Connection Pooling — SQLAlchemy 1.1 Documentation

効果無し。


Session.removeを呼ぶタイミング - スコトプリゴニエフスク通信

@soundkitchen さんに教えて貰った。

リクエストの最後は漏れる可能性があるから、リクエストの最初に remove() してセッションを作り直してみたら? というもの。

効果なし。


更新系ではでなくて、参照系で出るのはなにか違いがあるのかと思って、コードを見直したら、session.commit() しているかしていないかの違いがあった。

これかと思って、試してみたら、正解だった。


これで万事解決したかと思ったが、依然でる所があった。

因みにでたのが、WTForms で SQLAlchemy からセレクトボックスに DB の項目を出している所だった。


こんな感じで、セッションのクローズ漏れが出てくるのが怖い。

SQLAlchemy — The Pyramid Community Cookbook v0.2

Pyramid のドキュメントではリクエストコンテキストの最後に、必ず commit, close をしていた。

これにならって、以下のようにした。

@app.teardown_appcontext
def session_clear(exception):
    if exception and Session.is_active:
        session.rollback()
    else:
        #session.commit() 最初こうしてたけど、意図して Rollback した際に不具合おきるからなにもしない
        pass
    Session.close()

あと、session 生成時に、session.expire_on_commit = False しておかないと、例外が起きた。

DetachedInstanceError: Instance <MyModel at 0x36bb190> is not bound to a Session;
attribute refresh operation cannot proceed

なので、sessionmaker の際に expire_on_commit を付けるようにした。


とりあえずこれで安定して動くようになった。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/heavenshell/20160220/1455987788