2010-11-26 3 views
2

J'ai un objet modèle SQLAlchemy qui ressemble à ceci:Comment empêcher les insertions concurrentes de créer des éléments en double?

ResultModelBase = declarative_base() 
class Task(ResultModelBase): 
    """Task result/status.""" 

    id = sa.Column(sa.Integer, sa.Sequence("task_id_sequence"), 
        primary_key=True, 
        autoincrement=True) 
    task_id = sa.Column(sa.String(255)) 

Dans deux processus clients distincts, ce code est en cours d'exécution pour créer une nouvelle instance avec un task_id unique, il doit y avoir qu'une seule instance d'un task_id:

task = session.query(Task).filter(Task.task_id == task_id).first() 
if not task: 
    task = Task(task_id) 
    session.add(task) 
    session.flush() 

Comment puis-je réécrire ce code afin qu'il crée atomiquement une tâche avec l'ID spécifié?

Répondre

0

Utilisez des verrous autour du bloc de code.

# lock here 
task = session.query(Task).filter(Task.task_id == task_id).first() 
if not task: 
    task = Task(task_id) 
    session.add(task) 
    session.flush() 
# unlock here 
+0

Je préfère quelque chose de plus pratique que cela; pour un, verrouiller quoi? –

2

Votre problème est mal défini. Vous avez un défaut de conception dans votre base de données. Vous ne pouvez pas définir l'unicité de la ligne par son identifiant auto-incrémenté. Vous devez avoir d'autres champs qui le définissent comme unique (par exemple nom_tâche + worker), puis créer la tâche par ces champs: Task(task_name=smt, worker=smt) - ne pas mettre manuellement task_id - il sera automatiquement affecté comme ID de table suivant. Utilisez les clés primaires autoinc seulement pour la recherche et les relations - mais toujours mettre une clé unique composée d'autres tables. Si vous ne pouvez pas déterminer quels champs - votre table n'est pas normalisée.

Maintenant, le problème de la manipulation insertion de doublons: session.flush() # exécute les travaux précédents

session.flush() 
try: 
    t = Task(task_name=smt, worker=smt) 
    session.add() 
    session.flush() 
except sqlalchemy.exc.IntegrityError: 
    # task already exists 
else: 
    # task added to db 
+0

Ouais, ajouter une contrainte dans la base de données est certainement la bonne façon. Le 'task_id' devrait être déclaré unique (et devrait aussi avoir un index pour la vitesse):' task_id = sa.Column (sa.String (255), unique = True, index = True) ' –

+0

@ THC4k:' unique = True 'est de créer un index unique, donc' index = True' est redondant. –

Questions connexes