Mongo Distributed Lock¶
MongoDB connector enables distributed locking on MongoDB. It was tested on MongoDB v8.0.
Mongo Client
There is no need for a special MongoClient configuration. Default settings, where all writes use master node, are sufficient. Sherlock uses no read queries and only the following modification operations: findOneAndReplace
, findOneAndDelete
, deleteMany
.
Usage¶
Add dependencies to build.gradle.kts
:
Create sherlock instance and distributed lock:
import com.coditory.sherlock.DistributedLock;
import com.coditory.sherlock.Sherlock;
import com.coditory.sherlock.mongo.MongoSherlock;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MongoSyncLockSample {
private static final Logger logger = LoggerFactory.getLogger(MongoSyncLockSample.class);
private static MongoCollection<Document> getCollection() {
String database = "sherlock";
String connectionString = "mongodb://localhost:27017/" + database;
MongoClient mongoClient = MongoClients.create(connectionString);
return mongoClient
.getDatabase("sherlock")
.getCollection("locks");
}
public static void main(String[] args) {
Sherlock sherlock = MongoSherlock.create(getCollection());
DistributedLock lock = sherlock.createLock("sample-lock");
lock.runLocked(() -> logger.info("Lock acquired!"));
}
}
import com.coditory.sherlock.mongo.coroutines.MongoSherlock
import com.mongodb.kotlin.client.coroutine.MongoClient
import com.mongodb.kotlin.client.coroutine.MongoCollection
import kotlinx.coroutines.runBlocking
import org.bson.Document
import org.slf4j.Logger
import org.slf4j.LoggerFactory
object MongoKtLockSample {
private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
private fun getCollection(): MongoCollection<Document> {
val database = "sherlock"
val mongoClient = MongoClient.create("mongodb://localhost:27017/$database")
return mongoClient
.getDatabase(database)
.getCollection<Document>("locks")
}
private suspend fun sample() {
val sherlock = MongoSherlock.create(getCollection())
val lock = sherlock.createLock("sample-lock")
lock.runLocked { logger.info("Lock acquired!") }
}
@JvmStatic
fun main(args: Array<String>) {
runBlocking { sample() }
}
}
import com.coditory.sherlock.mongo.reactor.MongoSherlock;
import com.coditory.sherlock.reactor.DistributedLock;
import com.coditory.sherlock.reactor.Sherlock;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import com.mongodb.reactivestreams.client.MongoCollection;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MongoReactorLockSample {
private static final Logger logger = LoggerFactory.getLogger(MongoReactorLockSample.class);
private static MongoCollection<Document> getCollection() {
String database = "sherlock";
MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017/" + database);
return mongoClient
.getDatabase("sherlock")
.getCollection("locks");
}
public static void main(String[] args) {
Sherlock sherlock = MongoSherlock.create(getCollection());
DistributedLock lock = sherlock.createLock("sample-lock2");
lock.runLocked(() -> logger.info("Lock acquired!"))
.block();
}
}
import com.coditory.sherlock.mongo.rxjava.MongoSherlock;
import com.coditory.sherlock.rxjava.DistributedLock;
import com.coditory.sherlock.rxjava.Sherlock;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import com.mongodb.reactivestreams.client.MongoCollection;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MongoRxLockSample {
private static final Logger logger = LoggerFactory.getLogger(MongoRxLockSample.class);
private static MongoCollection<Document> getCollection() {
String database = "sherlock";
MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017/" + database);
return mongoClient
.getDatabase("sherlock")
.getCollection("locks");
}
public static void main(String[] args) {
Sherlock sherlock = MongoSherlock.create(getCollection());
DistributedLock lock = sherlock.createLock("sample-lock");
lock.runLocked(() -> logger.info("Lock acquired!"))
.blockingGet();
}
}
Learn more
See full source code example on Github.
Configuration¶
Configuration is available via sherlock builder:
Parameters:
clock
(default:Clock.systemUTC()
) - used to generate acquisition and expiration timestamps.lockDuration
(default:Duration.ofMinutes(5)
) - a default lock expiration time. If lock is not released and expiration time passes, the lock is treated as released.ownerIdPolicy
(default:uniqueOwnerId()
) - used to generate lock owner id. It's executed once for every lock, during lock creation. There are different policies available for generating lock ownerIds.locksCollection
- MongoDb collection used to store the locks.
Locks collection¶
Sample lock document:
{
// Lock id
"_id": "lock-id",
// Owner id
"acquiredBy": "aec5229a-1728-4200-b8d1-14f54ed9ac78",
// Lock acquisition moment
"acquiredAt": {
"$date": "2024-03-20T08:03:02.231Z"
},
// Lock expiation time.
// Might be null for locks that do not expire
"expiresAt": {
"$date": "2024-03-20T08:08:02.231Z"
}
}