Verrous distribués

La méthode self.lock d'un service exécute un bloc de code avec un verrou distribué.

Il est garanti qu'aucun autre service sur l'ensemble des serveurs d'un cluster n'exécutera simultanément un code protégé par un tel verrou tant que celui-ci existe. Cela permet d'exécuter du code qui doit être sérialisé et ne peut être exécuté en parallèle.

Les verrous sont nommés - par défaut, le nom d'un verrou est le nom du service qui le crée, mais les utilisateurs sont libres de choisir n'importe quel nom, ce qui permet de créer des interactions avancées entre les verrous et les services.

Les services peuvent soit attendre qu'un verrou soit disponible, soit renoncer immédiatement si un autre service en détient déjà un.

Tous les verrous finissent par s'épuiser et disparaître, de sorte qu'ils redeviennent disponibles avec le temps.

API et exemples d'utilisation

Sérialiser l'accès au même service

Utile si un service ne peut pas être invoqué simultanément, par exemple - s'il accède à une ressource qui ne peut pas gérer des requêtes parallèles.

Utiliser self.lock() seul est égal à self.lock(self.name) - c'est-à-dire que le nom du service devient le nom du verrou dans ce cas.

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class UpdateCustomer(Service):
    """ Updates customer data in CRM.
    """
    def handle(self):

        # A block of code that cannot run concurrently because,
        # for instance, the remote end cannot handle it properly.
        with self.lock():

            # Supposing there is an outgoing REST connection to a remote CRM

            conn = self.outgoing.plain_http['CRM'].conn
            conn.send(self.cid, {'cust_id':123})

Sérialiser l'accès à travers plusieurs services

Les verrous peuvent recevoir des noms arbitraires, ce qui leur permet d'être détenus par des services qui n'ont pas de lien entre eux et qui n'ont même pas besoin de connaître l'existence de l'autre.

Le nom peut provenir de n'importe où - ci-dessous, il est tiré de l'entrée SimpleIO d'un service, le résultat net étant que pour un compte client donné, un seul service dans tout le cluster peut être exécuté.

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class UpdateAccount(Service):
    """ Updates an account in CRM.
    """
    class SimpleIO:
        input_required = ('account_no', 'name')

    def handle(self):

        with self.lock(self.request.input.account_no):

            # Update the account here ..
            pass
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class DeleteAccount(Service):
    """ Deletes an account in CRM.
    """
    class SimpleIO:
        input_required = ('account_no',)

    def handle(self):

        with self.lock(self.request.input.account_no):

            # Delete the account here ..
            pass

Expiration du verrou

Par défaut, un verrou expire au bout de 20 secondes. C'est-à-dire que si un bloc de code ne se termine pas dans les 20 secondes, le verrou expirera de toute façon et il est possible qu'un autre service le détienne même si le service original n'est pas encore terminé.

Si un délai d'expiration plus long est nécessaire, indiquez-le en entrée, comme ci-dessous :

# Expires in 3 minutes
with self.lock(expires=180):
    ...

Attente de verrous

Par défaut, si un verrou ne peut pas être obtenu dans les 10 secondes, une exception sera levée - vous devez catcher l'exception générique Python.

Il est possible de fournir des informations sur le temps à attendre avant d'abandonner ou de ne pas attendre du tout si un verrou ne peut être créé.

# Wait half a minute for the lock, afterwards raise the exception
with self.lock(timeout=30):
    ...
  # Don't wait at all if we cannot have the lock, raise the exception straightaway
  with self.lock(timeout=0):
    ...