5
minutes
Mis à jour le
3/4/2025


Share this post

Découvrez comment utiliser ShedLock pour éviter l'exécution concurrente des tâches planifiées dans une architecture Spring Boot multi-instances. Guide complet avec configuration, bonnes pratiques et exemple de code.

#
ShedLock
#
Spring Boot
#
Cron
#
Scheduled
Loïc Broquet
Principal Engineer

Introduction

Pour les applications d'entreprise, en 2025 l’exécution de tâches planifiées est un besoin courant : génération de rapports, traitements par lots, synchronisation de données, etc.

Avec Spring Boot il suffit d’annoter une opération avec @Scheduled pour déclencher des traitements à heure fixe ou à intervalles réguliers.

Cependant l'adoption croissante des architectures distribuées et le déploiement d'applications multi-instances pour la haute disponibilité fait émerger un nouveau défi : comment éviter l'exécution simultanée d'une même tâche planifiée sur toutes les instances ?

Le problème des applications multi-instances

Risques et impacts

Imaginons un scénario où votre application, déployée sur trois instances, doit générer un rapport quotidien. Sans mécanisme de synchronisation, chaque instance va tenter d'exécuter la tâche, conduisant à :

  • Une consommation inutile de ressources
  • Des conflits potentiels sur les données
  • Des incohérences dans les résultats
  • Une multiplication des traitements

ShedLock comme solution

Présentation et principe de fonctionnement

ShedLock, développé par Lukáš Křečan, apporte une solution à ce problème. Son principe est simple mais efficace : utiliser un verrou centralisé pour s'assurer qu'une seule instance exécute la tâche planifiée à un moment donné.

Architecture

L'architecture de ShedLock s'articule autour de quelques composants clés :

  • MethodProxyScheduledLockAdvisor : le proxy qui bloque l’exécution de la méthode annotée si le verrou est mis
  • Le LockProvider : composant qui fourni la base de données de verrous. Il doit être configuré par le développeur. Le point d’attention principal : la base doit être partagée par toutes les instances de l’application.

Implémentation

Configuration détaillée

Voici un exemple complet de configuration de ShedLock. Ici on choisit de stocker les verrous dans la base de donnée car elle est partagée entre toutes les instances de l’application. On choisit donc l’implémentation JdbcTemplateLockProvider de LockProvider

💡 Astuce : vous pouvez mettre l’annotation @SchedulerLock sur n’importe quelle méthode, même si elle n’est pas annotée @Scheduled . Ainsi vous pouvez par exemple bloquer seulement une partie d’un traitement planifié.Est-ce une bonne idée ? C’est une autre histoire.

Points clés de la configuration

  1. @EnableSchedulerLock : Active le mécanisme de verrou avec une durée maximale par défaut
  2. JdbcTemplateLockProvider : L’implémentation de LockProvider.Cette implémentation stocke les verrous dans la base de donnée partagée entre les instances de l’application. Le développeur doit fournir :
    • La source de données
    • Le fuseau horaire (ici on choisit le même fuseau que la base, typiquement UTC)
  3. @SchedulerLock : Configure chaque tâche avec :
    • Le nom du verrou
    • Des tâches qui ne sont pas  en conflit peuvent toujours s’exécuter en prallèle si leurs verrous ont des noms différents.
    • Une durée minimale de verrou
    • La durée minimale permet de se prémunir des instances dont les horloges seraient légèrement désynchronisées
    • Une durée maximale de verrou
    • La durée maximale permet qu’une tâche plantée n’entrave pas les créneaux d’exécution suivants.

Tests et validation

Tests d'intégration avec TestContainers

Pour les tests d'intégration, l'utilisation de TestContainers permet de reproduire un environnement proche de la production :

Le piège du cache Registry

Comme nous l'avons découvert lors de nos tests, le Registry de ShedLock peut créer des situations inattendues. Voici les points importants à retenir :

  1. Le Registry est un cache en mémoire qui persiste durant toute la durée de vie de l'application
  2. Il faut penser à le nettoyer entre les tests avec lockProvider.clearCache()
  3. En production, ce cache aide à optimiser les performances en évitant des requêtes inutiles

Supervision et maintenance

Monitoring des verrous

Pour une supervision efficace, surveillez :

  • La table shedlock pour détecter les verrous orphelins
  • Les logs d'application pour les échecs d'acquisition de verrous
  • Les durées d'exécution des tâches par rapport aux timeouts configurés

Bonnes pratiques

  1. Toujours configurer des timeouts adaptés à vos traitements
  2. Nettoyer régulièrement les verrous obsolètes
  3. Implémenter une gestion d'erreurs robuste
  4. Monitorer les exécutions et les verrous
  5. Utiliser un identifiant de verrou explicite pour chaque traitement

Conclusion

ShedLock résout élégamment le problème des tâches planifiées en environnement distribué. Sa mise en place est simple, mais requiert une attention particulière aux détails de configuration et aux pièges potentiels, notamment au niveau du cache Registry.

Son intégration avec Spring Boot et sa flexibilité en termes de stockage (base de données, Redis, MongoDB, etc.) en font un choix pertinent pour sécuriser vos traitements planifiés en architecture multi-instances.

N'oubliez pas de consulter la documentation officielle de ShedLock pour les dernières mises à jour et options de configuration.