공유 리눅스 호스트에서 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():
# [...]
$ 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))