2016-04-04 23 views
0

私たちには、SQLAlchemyを使用してAWS MySQL MultiAZ RDSインスタンスからデータを読み書きするPythonサーバーがあります。AWSでSQLAlchemy接続がハングするフェイルオーバー付きのRDSリブート

フェイルオーバーの再起動をトリガするたびに、開いていた接続がどこにあるかを尋ねて、ステートメントを無期限に送信するような現象が発生しています。これはAWSのドキュメントによれば期待されるものですが、PythonのMySQLコネクタがこのような状況に対処できると期待しています。

私たちがウェブで見つけた最も近いケースは、この問題について話しているPostgres RDSに関する解決策google groups threadです。

たとえば、以下のスクリプトは、フェールオーバーのリブートを開始するときに無期限にハングします(上記のGoogleグループのスレッドを参照)。

from datetime import datetime 
from time import time, sleep 
from sqlalchemy import Column, Integer, String, create_engine 
from sqlalchemy.orm import sessionmaker 
from sqlalchemy.orm.scoping import scoped_session 
from sqlalchemy.ext.declarative import declarative_base 

import logging 

current_milli_time = lambda: int(round(time() * 1000)) 
Base = declarative_base() 

logging.basicConfig(format='%(asctime)s %(filename)s %(lineno)s %(process)d %(levelname)s: %(message)s', level="INFO") 

class Message(Base): 
    __tablename__ = 'message' 
    id = Column(Integer, primary_key=True) 
    body = Column(String(450), nullable=False) 

engine = create_engine('mysql://<username>:<password>@<db_host>/<db_name>',echo=False, pool_recycle=1800,) 
session_maker = scoped_session(sessionmaker(bind=engine, autocommit=False, autoflush=False)) 
session = session_maker() 

while True: 
    try: 
     ids = '' 
     start = current_milli_time() 
     for msg in session.query(Message).order_by(Message.id.desc()).limit(5): 
      ids += str(msg.id) + ', ' 
      logging.info('({!s}) (took {!s} ms) fetched ids: {!s}'.format(datetime.now().time().isoformat(), current_milli_time() - start, ids)) 

     start = current_milli_time() 
     m = Message() 
     m.body = 'some text' 
     session.add(m) 
     session.commit() 
     logging.info('({!s}) (took {!s} ms) inserted new message'.format(datetime.now().time().isoformat(), current_milli_time() - start)) 

    except Exception, e: 
     logging.exception(e) 
     session.rollback() 
    finally: 
     session_maker.remove() 

    sleep(0.25) 

私たちは、接続タイムアウトで遊んでみましたが、問題がAWSは、フェールオーバーインスタンスに切り替えたら、単純にハングアップし、既にオープンされた接続に関連しているようです。

私たちの質問です - この問題が発生した人がいますか?

+0

クライアントにシームレスになることは間違いありません。 [docs](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_RebootInstance.html)から: "DBインスタンスのフェールオーバーを強制すると、Amazon RDSは自動的に別のインスタンスのスタンバイレプリカに切り替えますDBインスタンスがスタンバイDBインスタンスを指すようにDNSレコードを更新します。その結果、DBインスタンスへの既存の接続をすべてクリーンアップして再確立する必要があります。 – univerio

+0

あなたはまったく正しいです、私はPython DBコネクタがこの状況を検知する方法を持っているのだろうかと疑問に思っています – wilfo

+0

どのように接続タイムアウトを設定していますか?問題が既存の接続である場合、シャットダウン時にリモートホストがFINパケットを送信していないように思えます。これは心配です。通常、ホストを正常にシャットダウンすると、アプリケーションはソケット接続を正常に終了します。途中のルータがTCP RSTパケットをドロップしていない限り、接続はハングアップしません。 – univerio

答えて

1

IMHO、switchcoverを処理するSQLコネクタのタイムアウトを使用すると黒い魔法のようです。各コネクタは、常に動作が異なり、診断が難しい。

@univerioコメントをもう一度読むと、AWSはSAME RDSエンドポイント名の新しいIPアドレスを再割り当てします。スイッチングを実行している間は、RDSエンドポイント名と古いIPアドレスはサーバーインスタンスのDNSキャッシュ内に残ります。これはDNSキャッシュの問題です。そのため、AWSはあなたに「クリーンアップ....」を依頼しています。

DNSを再度読み取るためにSQLAlchemyを再起動しない限り、セッションが何か起こっていることを知る方法はなく、動的に切り替える方法はありません。そして、最悪の場合、SQLAlchemyで使用されるコネクタで問題が発生する可能性があります。

IMHO、コード内の切り替えに対処する価値はありません。ラムダのようなAWSサービスを購読するだけで、イベントを切り替えることができ、新しいIPアドレスを反映すると思われる接続を再起動するようにアプリケーションサーバーを起動します。

関連する問題