Lockedb is a robust, customizable lock management system designed for managing distributed locks across various databases, primarily intended for use with Minecraft plugins.
- Overview
- System Requirements
- Installation
- Setup
- Usage
- Supported Databases
- Configuration Options
- Examples
Lockedb provides a simple yet powerful API for distributed locking, allowing you to synchronize access to resources across multiple servers or instances. It features:
- Asynchronous operations with CompletableFuture
- Configurable timeouts and expiration
- Optional password protection
- Support for multiple database backends
Add Lockedb with your preferred database to your project's dependencies. For example, with MongoDB:
repositories {
maven { url = 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.h14turkiye:lockedb-mongodb:main-SNAPSHOT'
// Include MongoDB dependencies
implementation 'org.mongodb:bson:4.9.1'
implementation 'org.mongodb:mongo-java-driver:3.12.10'
}To avoid dependency conflicts, use the Shadow plugin to relocate dependencies:
shadowJar {
relocate 'com.h14turkiye.lockedb', 'your.org.app.relocated.lockedb'
// Preferably relocate MongoDB and BSON as well
relocate 'com.mongodb', 'your.org.app.relocated.mongodb'
relocate 'org.bson', 'your.org.app.relocated.bson'
}Initialize the lock builder for your database. Currently, MongoDB is supported:
// Get your database connection
MongoDatabase db = ...;
// Create a lock factory
LockFactory factory = new MongoLockFactory(db);Create and configure a lock using the builder pattern:
// Create a lock with custom configuration
ALock lock = factory.builder()
.expiresAfterMS(60000L) // Lock expires after 1 minute
.timeoutMS(5000L) // Wait up to 5 seconds to acquire
.password("optional-secret") // Optional password protection
.build("resource-key"); // Unique resource identifierOr with a different approach:
ALock lock = factory.createLock("resource-key");
lock.setExpiresAfterMS(60000L);
lock.setTimeoutMS(5000L);
lock.setPassword("optional-secret");Use the lock asynchronously with CompletableFuture:
// Acquire the lock asynchronously
lock.acquire().thenAccept(success -> {
if (success) {
System.out.println("Lock acquired!");
// Perform operations requiring the lock
// Release the lock when done
lock.release().thenAccept(released -> {
if (released) {
System.out.println("Lock released successfully");
}
});
} else {
System.out.println("Failed to acquire lock");
}
});Check if a resource is currently locked or available:
// Check if the resource is currently locked
lock.isLocked().thenAccept(locked -> {
System.out.println("Resource is " + (locked ? "locked" : "available"));
});
// Check if the lock can be acquired
lock.isAcquirable().thenAccept(acquirable -> {
System.out.println("Lock is " + (acquirable ? "acquirable" : "not acquirable"));
});Currently supported databases:
- MongoDB
- Redis/Dragonfly
| Option | Description | Default |
|---|---|---|
expiresAfterMS |
Time after which the lock automatically expires | 24 hours |
timeoutMS |
Maximum time to wait when acquiring a lock | 30 seconds |
password |
Optional password for lock protection | None |
The password parameter is optional - if not set, no password will be used for authentication. When a password is provided, it enables a special feature: a lock that is already acquired can be re-acquired by another process if the correct password is provided.
This is useful in scenarios where:
- You need to transfer lock ownership between processes
- A specific admin process needs to override existing locks
- You want to implement hierarchical locking with master/slave processes
Example of using password protection:
// Process 1: Create and acquire a password-protected lock
ALock lockWithPassword = builder
.password("master-key")
.build("protected-resource");
lockWithPassword.acquire().thenAccept(acquired -> {
System.out.println("Process 1: Lock acquired: " + acquired);
});
// Process 2: Can acquire the same lock if it knows the password
ALock sameResourceLock = builder
.password("master-key") // Must match the password from Process 1
.build("protected-resource");
sameResourceLock.acquire().thenAccept(acquired -> {
// This will succeed if the password matches, even though
// Process 1 already holds the lock
System.out.println("Process 2: Lock acquired: " + acquired);
});
// Process 3: Cannot acquire the lock without the correct password
ALock lockWithWrongPassword = builder
.password("wrong-key")
.build("protected-resource");
lockWithWrongPassword.acquire().thenAccept(acquired -> {
// This will fail because the password doesn't match
System.out.println("Process 3: Lock acquired: " + acquired);
});Complete example with error handling:
// Create a lock
ALock lock = builder
.expiresAfterMS(120000L)
.timeoutMS(10000L)
.build("player-inventory");
// Try to acquire the lock
lock.acquire()
.thenCompose(acquired -> {
if (!acquired) {
System.out.println("Could not acquire lock - resource busy");
return CompletableFuture.completedFuture(false);
}
try {
// Perform critical operation here
System.out.println("Lock acquired, performing operation...");
// Simulate work
Thread.sleep(1000);
return lock.release();
} catch (Exception e) {
e.printStackTrace();
return lock.release();
}
})
.exceptionally(ex -> {
ex.printStackTrace();
return false;
});