[python] 최대 횟수까지 무언가를 시도하는 비단뱀적인 방법이 있습니까? [복제]

공유 리눅스 호스트에서 MySQL 서버를 쿼리하는 파이썬 스크립트가 있습니다. 어떤 이유로 MySQL에 대한 쿼리는 종종 “server has gone away”오류를 반환합니다.

_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')

나중에 즉시 쿼리를 다시 시도하면 일반적으로 성공합니다. 그래서 저는 파이썬에서 쿼리를 실행하는 합리적인 방법이 있는지 알고 싶습니다. 실패하면 고정 된 횟수까지 다시 시도합니다. 아마 포기하기 전에 5 번 시도하고 싶을 것입니다.

내가 가진 종류의 코드는 다음과 같습니다.

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

try:
    cursor.execute(query)
    rows = cursor.fetchall()
    for row in rows:
        # do something with the data
except MySQLdb.Error, e:
    print "MySQL Error %d: %s" % (e.args[0], e.args[1])

저는 except 절에서 또 다른 시도를함으로써 그것을 할 수 있습니다.하지만 그것은 믿을 수 없을 정도로 추하고 이것을 달성하기위한 적절한 방법이 있어야한다고 느낍니다.



답변

어때 :

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()
attempts = 0

while attempts < 3:
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        break
    except MySQLdb.Error, e:
        attempts += 1
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])


답변

Dana의 답변을 바탕으로 데코레이터로 이것을 할 수 있습니다.

def retry(howmany):
    def tryIt(func):
        def f():
            attempts = 0
            while attempts < howmany:
                try:
                    return func()
                except:
                    attempts += 1
        return f
    return tryIt

그때…

@retry(5)
def the_db_func():
    # [...]

decorator모듈 을 사용하는 향상된 버전

import decorator, time

def retry(howmany, *exception_types, **kwargs):
    timeout = kwargs.get('timeout', 0.0) # seconds
    @decorator.decorator
    def tryIt(func, *fargs, **fkwargs):
        for _ in xrange(howmany):
            try: return func(*fargs, **fkwargs)
            except exception_types or Exception:
                if timeout is not None: time.sleep(timeout)
    return tryIt

그때…

@retry(5, MySQLdb.Error, timeout=0.5)
def the_db_func():
    # [...]

설치하려면 모듈 :decorator

$ easy_install decorator


답변

업데이트 : 더 많은 기능을 지원하고 일반적으로 더 유연한 재시도 라이브러리 인 tenacity 라는 더 나은 유지 관리 포크가 있습니다.


예, 결합 할 수있는 여러 종류의 재시도 로직을 구현하는 데코레이터 가있는 재시도 라이브러리 가 있습니다.

몇 가지 예 :

@retry(stop_max_attempt_number=7)
def stop_after_7_attempts():
    print "Stopping after 7 attempts"

@retry(wait_fixed=2000)
def wait_2_s():
    print "Wait 2 second between retries"

@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
def wait_exponential_1000():
    print "Wait 2^x * 1000 milliseconds between each retry,"
    print "up to 10 seconds, then 10 seconds afterwards"


답변

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

for i in range(3):
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        break
    except MySQLdb.Error, e:
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])


답변

다음과 같이 리팩토링하겠습니다.

def callee(cursor):
    cursor.execute(query)
    rows = cursor.fetchall()
    for row in rows:
        # do something with the data

def caller(attempt_count=3, wait_interval=20):
    """:param wait_interval: In seconds."""
    conn = MySQLdb.connect(host, user, password, database)
    cursor = conn.cursor()
    for attempt_number in range(attempt_count):
        try:
            callee(cursor)
        except MySQLdb.Error, e:
            logging.warn("MySQL Error %d: %s", e.args[0], e.args[1])
            time.sleep(wait_interval)
        else:
            break

아웃 고려해 callee함수는 재시도 코드에서 수렁에 빠져받지 않고 비즈니스 로직을 쉽게 알 수 있도록 기능을 깰 것으로 보인다.


답변

S.Lott처럼, 우리가 완료되었는지 확인하는 깃발을 좋아합니다.

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

success = False
attempts = 0

while attempts < 3 and not success:
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        success = True
    except MySQLdb.Error, e:
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])
        attempts += 1


답변

def successful_transaction(transaction):
    try:
        transaction()
        return True
    except SQL...:
        return False

succeeded = any(successful_transaction(transaction)
                for transaction in repeat(transaction, 3))