2014-05-16 20 views
16

Ho cercato per un po 'di tempo e non riesco a trovare una soluzione al mio problema. Stiamo utilizzando SQLAlchemy in collaborazione con MySQL per il nostro progetto e ci incontriamo più volte l'errore temuto:Retry on deadlock per MySQL/SQLAlchemy

1213, 'Deadlock found when trying to get lock; try restarting transaction'.

Vorremmo provare a riavviare la transazione al massimo tre volte in questo caso.

Ho iniziato a scrivere un decoratore che esegue questa operazione ma non so come salvare lo stato della sessione prima del fail e riprovare la stessa transazione dopo di essa? (Come SQLAlchemy richiede un rollback ogni volta che viene sollevata un'eccezione)

Il mio lavoro fino ad ora,

def retry_on_deadlock_decorator(func): 
    lock_messages_error = ['Deadlock found', 'Lock wait timeout exceeded'] 

    @wraps(func) 
    def wrapper(*args, **kwargs): 
     attempt_count = 0 
     while attempt_count < settings.MAXIMUM_RETRY_ON_DEADLOCK: 
      try: 
       return func(*args, **kwargs) 
      except OperationalError as e: 
       if any(msg in e.message for msg in lock_messages_error) \ 
         and attempt_count <= settings.MAXIMUM_RETRY_ON_DEADLOCK: 
        logger.error('Deadlock detected. Trying sql transaction once more. Attempts count: %s' 
           % (attempt_count + 1)) 
       else: 
        raise 
      attempt_count += 1 
    return wrapper 
+1

Ti ho aiutato? O hai trovato un'altra soluzione. Si prega di condividere qualsiasi risultato. –

risposta

0

Hai usato codice come questo?

try: 

    Perform table transaction 
    break 
except: 
    rollback 
    delay 
    try again to perform table transaction 

The only way to truly handle deadlocks is to write your code to expect them. This generally isn't very difficult if your database code is well written. Often you can just put a try/catch around the query execution logic and look for a deadlock when errors occur. If you catch one, the normal thing to do is just attempt to execute the failed query again.

Link utili:

2

Non si può davvero fare quello con il Session dall'esterno. Session dovrebbe supportare questo internamente. Significherebbe salvare un sacco di stato privato, quindi questo potrebbe non valere il tuo tempo.

Ho completamente abbandonato la maggior parte degli oggetti ORM in favore dell'interfaccia SQLalchemy Core di livello inferiore. Usando questo (o anche qualsiasi altra interfaccia dbapi) puoi usare banalmente il tuo decoratore retry_on_deadlock_decorator (vedi la domanda sopra) per creare un wrapper db.execute compatibile con il nuovo tentativo.

@retry_on_deadlock_decorator 
def deadlock_safe_execute(db, stmt, *args, **kw): 
    return db.execute(stmt, *args, **kw) 

E invece di

db.execute("UPDATE users SET active=0") 

fate

deadlock_safe_execute(db, "UPDATE users SET active=0") 

che ripetere automaticamente se un deadlock accade.