Version de développement
Cette documentation décrit la version « next » (develop), non encore publiée. Pour la version stable, basculez sur « v5 » dans l'en-tête.
Développeurs

Verrous advisory

Utiliser frappe.db.advisory_lock pour qu'un seul processus à la fois exécute un traitement sur une ressource logique.

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ètreEffet
keyNom du verrou (texte). Dodock le hache, donc une clé longue ne pose pas de problème
timeoutAttente 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.

BaseMécanisme
PostgreSQLpg_try_advisory_lock, puis pg_advisory_unlock à la sortie
MariaDBGET_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
SQLiteNon 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).