8

アップデート3/4:

私はいくつかのテストを行い、チェックアウトイベントハンドラーを使用して切断をチェックすることがElixirで機能することを証明しました。私の問題はサブプロセスからの呼び出しと関係があると考え始めましたsession.commit()か?更新:サブプロセスを呼び出して自分自身を反証session.commit()しました。以下の例を更新しました。マルチプロセッシングモジュールを使用してサブプロセスを作成しています。

これがどのように機能するかを示すコードです(pool_recycle!を使用しなくても):

from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy.pool import Pool
from elixir import *
import multiprocessing as mp

class SubProcess(mp.Process):
    def run(self):
        a3 = TestModel(name="monkey")
        session.commit()

class TestModel(Entity):
    name = Field(String(255))

@event.listens_for(Pool, "checkout")
def ping_connection(dbapi_connection, connection_record, connection_proxy):
    cursor = dbapi_connection.cursor()
    try:
        cursor.execute("SELECT 1")
    except:
        # optional - dispose the whole pool
        # instead of invalidating one at a time
        # connection_proxy._pool.dispose()

        # raise DisconnectionError - pool will try
        # connecting again up to three times before raising.
        raise exc.DisconnectionError()
    cursor.close()

from sqlalchemy import create_engine
metadata.bind = create_engine("mysql://foo:bar@localhost/some_db", echo_pool=True)
setup_all(True)

subP = SubProcess()

a1 = TestModel(name='foo')
session.commit()

# pool size is now three.

print "Restart the server"
raw_input()

subP.start()

#a2 = TestModel(name='bar')
#session.commit()

アップデート2:

MySQLのポスト1.2.2バージョンとして別の解決策を見つけることを余儀なくされています-pythonは再接続パラメータのサポートを削除します。誰かが解決策を手に入れましたか?:\

アップデート1(古いソリューション、MySQL-pythonバージョン> 1.2.2では機能しません):

解決策を見つけました:呼び出しに渡すconnect_args={'reconnect':True}create_engine問題が修正され、自動的に再接続されます。チェックアウトイベントハンドラーも必要ないようです。

したがって、質問の例では、次のようになります。

metadata.bind = create_engine("mysql://foo:bar@localhost/db_name", pool_size=100, pool_recycle=3600, connect_args={'reconnect':True})

元の質問:

この問題に対してかなりのグーグルを実行しましたが、Elixirに固有の解決策が見つからなかったようです-SQLAlchemyドキュメントの「切断処理-悲観的」の例を使用してMySQL切断を処理しようとしています。ただし、これをテストすると(MySQLサーバーを再起動して)、チェックアウトイベントハンドラーの前に「MySQLサーバーがなくなりました」というエラーが発生します。

elixirを初期化するために使用するコードは次のとおりです。

##### Initialize elixir/SQLAlchemy
# Disconnect handling
from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy.pool import Pool

@event.listens_for(Pool, "checkout")
def ping_connection(dbapi_connection, connection_record, connection_proxy):
    logging.debug("***********ping_connection**************")
    cursor = dbapi_connection.cursor()
    try:
        cursor.execute("SELECT 1")
    except:
        logging.debug("######## DISCONNECTION ERROR #########")            
        # optional - dispose the whole pool
        # instead of invalidating one at a time
        # connection_proxy._pool.dispose()

        # raise DisconnectionError - pool will try
        # connecting again up to three times before raising.
        raise exc.DisconnectionError()
    cursor.close()

metadata.bind= create_engine("mysql://foo:bar@localhost/db_name", pool_size=100, pool_recycle=3600)

setup_all()

elixirエンティティオブジェクトを作成して保存するとsession.commit()、上記で定義したイベントから生成された「ping_connection」メッセージが表示されます。ただし、mysqlサーバーを再起動して再度テストすると、ping接続イベントの直前にmysqlサーバーが消えたというメッセージが表示されて失敗します。

関連する行から始まるスタックトレースは次のとおりです。

  File "/usr/local/lib/python2.6/dist-packages/elixir/entity.py", line 1135, in get_by
    return cls.query.filter_by(*args, **kwargs).first()
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 1963, in first
    ret = list(self[0:1])
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 1857, in __getitem__
    return list(res)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 2032, in __iter__
    return self._execute_and_instances(context)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 2047, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1399, in execute
    params)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1532, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1640, in _execute_context
    context)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1633, in _execute_context
    context)
  File "/usr/local/lib/python2.6/dist-packages/sqlalchemy/engine/default.py", line 330, in do_execute
    cursor.execute(statement, parameters)
  File "/usr/lib/pymodules/python2.6/MySQLdb/cursors.py", line 166, in execute
    self.errorhandler(self, exc, value)
  File "/usr/lib/pymodules/python2.6/MySQLdb/connections.py", line 35, in defaulterrorhandler
    raise errorclass, errorvalue
OperationalError: (OperationalError) (2006, 'MySQL server has gone away') 
4

4 に答える 4

2

実際の問題は、セッションメーカーファクトリを呼び出すたびに同じセッションを提供するsqlalchemyです。このため、セッションを呼び出さない限り、以前に開いたセッションで後のクエリが実行される可能性がありますsession.remove()remove()ただし、セッションをリクエストするたびに呼び出すことを覚えておく必要があるのは楽しいことではなく、sqlalchemyははるかに単純なものを提供します。それはコンテクストの「スコープ付き」セッションです。

スコープセッションを作成するには、セッションメーカーをラップするだけです。

from sqlalchemy.orm import scoped_session, sessionmaker
Session = scoped_session(sessionmaker())

このようにして、ファクトリを呼び出すたびにコンテクストバウンドセッションを取得しsession.remove()ます。つまり、呼び出し元の関数が終了するとすぐにsqlalchemyがあなたに代わって呼び出します。ここを参照してください:sqlalchemy-コンテキストセッションの寿命

于 2012-07-26T15:01:50.970 に答える
2

session.remove()最後の回避策は、エリクサーエンティティを操作してロードする前にメソッドの開始を呼び出すことでした。これにより、接続がプールに返されるため、再度使用されると、プールのチェックアウトイベントが発生し、ハンドラーが切断を検出します。SQLAlchemyのドキュメントから

リクエストの最後にセッションを削除する必要は厳密にはありません。他のオプションには、既存のセッションがプールへの接続を返すように、最後にSession.close()、Session.rollback()、Session.commit()を呼び出すことが含まれます。既存のトランザクションコンテキストを削除します。個々のコントローラーメソッドが、要求の終了後にトランザクションが開いたままにならないようにする責任がある場合は、何もしないこともオプションです。

非常に重要な小さな情報が、エリクサーのドキュメントで言及されていればいいのにと思います。しかし、SQLAlchemyの予備知識があることを前提としていますか?

于 2011-11-02T06:48:50.377 に答える
0

(mysqldの再起動の前後の)両方の操作に同じセッションを使用していますか?その場合、"checkout"イベントは新しいトランザクションが開始されたときにのみ発生します。呼び出すとcommit()、新しいトランザクションが開始され(自動コミットモードを使用しない場合)、接続がチェックアウトされます。したがって、チェックアウト後にmysqldを再起動します。

2番目の操作の直前(およびmysqldの再起動後)にハックするcommit()か呼び出すだけで、問題が解決するはずです。rollback()それ以外の場合は、前回のコミット後に長時間待機するたびに、新しい新しいセッションを使用することを検討してください。

于 2011-10-27T10:27:22.570 に答える
0

これが私が抱えていたのと同じ問題であるかどうかはわかりませんが、ここに行きます:

私が遭遇したときMySQL server has gone away、私はそれを使用して解決しました。http://www.sqlalchemy.org/docs/dialects/mysql.html#connection-timeoutscreate_engine(..., pool_recycle=3600)を参照してください。

于 2011-10-31T19:51:48.390 に答える