Verrous advisory
Verrous advisory
Un verrou advisory est un verrou nommé, tenu par la base de données. Il ne verrouille aucune ligne. Deux processus qui demandent le même nom s'attendent l'un l'autre. Utilisez-le pour qu'un seul processus à la fois exécute un traitement : un réapprovisionnement d'article, la génération d'un fichier, une synchronisation.
frappe.db.advisory_lock
frappe.db.advisory_lock(key, *, timeout=10)
C'est un gestionnaire de contexte. Il prend le verrou au début du bloc with et le libère à la sortie, même si le bloc lève une exception.
| Paramètre | Effet |
|---|---|
key | Nom du verrou (texte). Dodock le hache, donc une clé longue ne pose pas de problème |
timeout | Attente maximale en secondes. Après ce délai, Dodock lève frappe.QueryTimeoutError |
Le verrou est lié à la session de base de données, pas à la transaction. Il reste tenu après un frappe.db.commit() dans le bloc.
| Base | Mécanisme |
|---|---|
| PostgreSQL | pg_try_advisory_lock, puis pg_advisory_unlock à la sortie |
| MariaDB | GET_LOCK, puis RELEASE_LOCK à la sortie. Le nom est préfixé par la base du site, donc deux sites du même serveur ne partagent pas leurs verrous |
| SQLite | Non disponible : lève NotImplementedError |
Sur PostgreSQL, si une requête du bloc échoue, Dodock annule le travail du bloc jusqu'à un point de sauvegarde pris au début, puis libère le verrou.
Exemple
Exemple fictif : une méthode lance un réapprovisionnement. Deux clics simultanés ne doivent pas créer deux demandes de matériel.
import frappe
@frappe.whitelist()
def lancer_reapprovisionnement(item_code: str):
try:
with frappe.db.advisory_lock(f"reappro:{item_code}", timeout=5):
if frappe.db.exists("Material Request", {"docstatus": 0, "custom_article_reappro": item_code}):
return False
creer_demande_materiel(item_code) # fonction de votre application
return True
except frappe.QueryTimeoutError:
frappe.throw("Un réapprovisionnement est déjà en cours pour cet article.")
Le champ custom_article_reappro est un champ personnalisé de l'exemple.
Conseils
- Choisissez une clé précise (
reappro:<article>) plutôt qu'une clé générale qui bloque tous les traitements. - Gardez le bloc court. Les autres processus attendent pendant toute sa durée.
- Prenez toujours plusieurs verrous dans le même ordre, pour éviter un interblocage.
- Sur PostgreSQL, pour un verrou libéré à la fin de la transaction, utilisez
frappe.db.transaction_advisory_lock(voir Verrous transactionnels dans PostgreSQL).