Librairie Java pour permettre une exécution mutex de vos charges de travail dans le cloud.
🚀 Pourquoi adopter
r3edge-mini-lock?✅ Autoconfiguration avec création automatique de la table de gestion des locks
✅ 1 API ultra-simple : Demandez un lock par nom, exécutez, relâchez, c’est tout
✅ 100 % Spring Boot 3.x (utilise JPA pour la persistence)
✅ Vous permet de couvrir simplement les principaux Cloud Design Patterns de gestion concurrente :✅ Parfaitement complémentaire avec
r3edge-task-dispatcherpour garantir l’exclusivité des tâches planifiées en environnement distribué.
This project is documented in French 🇫🇷 by default.
An auto-translated English version is available here:
👉 English (auto-translated by Google)
- ✅ Pose d'un verrou en 1 ligne de code
- ✅ Libération explicite du lock ou expiration automatique
- ✅ Nettoyage automatique des locks expirés avec délai d'expiration configurable
- ✅ Compatible avec toutes les bases supportées par Spring Data JPA
- ✅ Prêt à l’emploi grâce à l’autoconfiguration Spring Boot
repositories {
mavenCentral()
// Dépôt GitHub Packages de r3edge-mini-lock
maven {
url = uri("https://maven.pkg.github.com/dsissoko/r3edge-mini-lock")
credentials {
username = ghUser
password = ghKey
}
}
mavenLocal()
}
dependencies {
// Spring Boot
implementation 'org.springframework.boot:spring-boot-starter-web'
// Base de données
implementation 'org.postgresql:postgresql:42.7.7'
// Pour mini-lock
implementation "com.r3edge:r3edge-mini-lock:0.0.7"
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
testImplementation 'com.h2database:h2'
}
⚠️ Cette librairie est publiée sur GitHub Packages: Même en open source, GitHub impose une authentification pour accéder aux dépendances.
Il faudra donc valoriser ghUser et ghKey dans votre gradle.properties:
#pour récupérer des packages github
ghUser=your_github_user
ghKey=github_token_with_read_package_scoper3edge:
minilock: # pas de configuration à faire pour l'instant
# cleanup_expired_frequency: 10*60 # par défaut 15 minutes
spring:
datasource:
url: jdbc:postgresql://mainline.proxy.rlwy.net:24622/railway
username: app_user
password: big_strong_password
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
⚠️ Pour bénéficier de la fonction de nettoyage automatique des verrous, nous devez configurer votre app Spring Boot avec @EnableScheduling.
package com.example.demo;
import java.time.Duration;
import java.util.concurrent.Callable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.r3edge.minilock.ExecutionLockService;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
public class DemoService {
private static final String RESOURCE_TEASER = "teaser-ai-extraction";
private final ExecutionLockService lockService;
@Value("${spring.application.name:default-locker}")
private String locker;
// 👁🗨 MÉTHODE "MÉTIER" À PROTÉGER ABSOLUMENT
private void videoTeasingAIExtraction() {
log.info("🎬 Extraction teaser vidéo par IA en cours...");
// Simulation d'un gros traitement IA
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log.info("✅ Teaser généré !");
}
// 1️⃣ - Acquire/release manuel
public boolean extractTeaser_acquireRelease() {
long timeout = 60000;
boolean locked = lockService.acquireLock(RESOURCE_TEASER, locker, timeout);
if (locked) {
try {
videoTeasingAIExtraction();
return true;
} finally {
lockService.releaseLockNormal(RESOURCE_TEASER, locker);
}
} else {
log.warn("⛔ Teaser déjà en cours sur cette vidéo !");
return false;
}
}
// 2️⃣ - Protection auto via runIfUnlocked (Runnable)
public boolean extractTeaser_runIfUnlocked() {
return lockService.runIfUnlocked(
RESOURCE_TEASER,
Duration.ofSeconds(30),
this::videoTeasingAIExtraction
);
}
// 3️⃣ - Protection auto via runIfUnlockedThrowing (Callable<T>)
public String extractTeaser_runIfUnlockedThrowing() throws Exception {
return lockService.runIfUnlockedThrowing(
RESOURCE_TEASER,
Duration.ofSeconds(30),
(Callable<String>) () -> {
videoTeasingAIExtraction();
return "Teaser généré (avec retour)";
}
);
}
// Ajout d'un hook pour libérer le lock sur shutdown (ex: dernier teaser traité)
@PostConstruct
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("🛑 Shutdown hook: libération du lock pour resource={}", RESOURCE_TEASER);
boolean released = lockService.releaseLockOnShutdown(RESOURCE_TEASER, locker);
if (released) {
log.info("Lock sur '{}' libéré proprement à l'arrêt.", RESOURCE_TEASER);
} else {
log.warn("Aucun lock à libérer pour '{}', ou déjà libéré.", RESOURCE_TEASER);
}
}));
}
}ℹ️ Au 1er démarrage, mini lock va créer la table de verrou automatiquement:
Hibernate: create table execution_lock (lock_expires_at timestamp(6), locked_at timestamp(6), updated_at timestamp(6), lock_detail varchar(50) check (lock_detail in ('NORMAL_RELEASE','TIMEOUT_EXPIRED','FORCE_RELEASE_BY_ADMIN','SYSTEM_SHUTDOWN','ERROR_DURING_PROCESS')), locked_by varchar(255), resource varchar(255) not null, status varchar(255) check (status in ('LOCKED','RELEASED')), primary key (resource))Mini lock affiche des logs explicites en cas d'échec d'acquisition de verrou
2025-08-03T18:13:00.437+02:00 INFO 10484 --- [r3edge-mini-lock-starter] [ Thread-8] c.r3edge.minilock.ExecutionLockService : ✅ Lock acquis pour teaser-ai-extraction par r3edge-mini-lock-starter
2025-08-03T18:13:00.445+02:00 INFO 10484 --- [r3edge-mini-lock-starter] [ Thread-8] com.example.demo.DemoService : 🎬 Extraction teaser vidéo par IA en cours...
2025-08-03T18:13:00.472+02:00 ERROR 10484 --- [r3edge-mini-lock-starter] [ Thread-10] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: duplicate key value violates unique constraint "execution_lock_pkey"
Détail : Key (resource)=(teaser-ai-extraction) already exists.
2025-08-03T18:13:00.472+02:00 ERROR 10484 --- [r3edge-mini-lock-starter] [ Thread-9] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: duplicate key value violates unique constraint "execution_lock_pkey"
Détail : Key (resource)=(teaser-ai-extraction) already exists.
2025-08-03T18:13:00.649+02:00 WARN 10484 --- [r3edge-mini-lock-starter] [ Thread-10] c.r3edge.minilock.ExecutionLockService :
❌ LOCK CONCURRENT ACQUISITION FAILED
- Resource : teaser-ai-extraction
- Locker lost : default-locker
- Locker owner : r3edge-mini-lock-starter
- Status : LOCKED
2025-08-03T18:13:00.649+02:00 WARN 10484 --- [r3edge-mini-lock-starter] [ Thread-10] c.r3edge.minilock.ExecutionLockService : ⛔ Impossible d'acquérir le lock pour teaser-ai-extraction
2025-08-03T18:13:00.649+02:00 INFO 10484 --- [r3edge-mini-lock-starter] [ Thread-10] com.example.demo.DemoConcurrentRunner : Thread 3 (runIfUnlockedThrowing): LOCK FAIL
2025-08-03T18:13:00.650+02:00 WARN 10484 --- [r3edge-mini-lock-starter] [ Thread-9] c.r3edge.minilock.ExecutionLockService :
❌ LOCK CONCURRENT ACQUISITION FAILED
- Resource : teaser-ai-extraction
- Locker lost : default-locker
- Locker owner : r3edge-mini-lock-starter
- Status : LOCKED
2025-08-03T18:13:00.651+02:00 WARN 10484 --- [r3edge-mini-lock-starter] [ Thread-9] c.r3edge.minilock.ExecutionLockService : ⛔ Impossible d'acquérir le lock pour teaser-ai-extraction
2025-08-03T18:13:00.651+02:00 INFO 10484 --- [r3edge-mini-lock-starter] [ Thread-9] com.example.demo.DemoConcurrentRunner : Thread 2 (runIfUnlocked): LOCK FAIL
✅ Cette librairie a été conçue et testée avec :
- Java 17+
- Spring Boot 3.x
- Spring Data JPA
- Base de données SQL supportée (PostgreSQL, H2, MySQL, etc.)
- Rien: mini lock est volontairement minimaliste
- Proposer d'avantage d'options de configuration
- Messages i18n
📫 Maintenu par @dsissoko – contributions bienvenues.
