wrdv / scalalock   1.1.0

Apache License 2.0 GitHub

Distributed Lock library in scala. currently supported persistence stores: mongodb - depending on mongo-scala-driver

Scala versions: 2.12

Scalalock Maven Central

Distributed lock library in scala. based on mongodb (using reactivemongo or mongo-scala-driver)

consists of 3 modules:

  • scalalock-api - contains the api and the locking logic
  • scalalock-mongo - an api implementation using mongodb as the lock persistence store. implementation is dependant on mongo-scala-driver.
  • scalalock-reactivemongo - an alternative api implementation using reactivemongo as the mongodb driver. implementation is dependant on reactivemongo.

Usage

  1. Add dependencies to build.sbt:
  • Option A - if you're using mongo-scala-driver
    libraryDependencies ++= Seq(
      "com.weirddev" %% "scalalock-api" % "1.0.6",
      "com.weirddev" %% "scalalock-mongo" % "1.0.6"
    )
  • Option B - if you're using reactivemongo
    libraryDependencies ++= Seq(
      "com.weirddev" %% "scalalock-api" % "1.0.6",
      "com.weirddev" %% "scalalock-reactivemongo" % "1.1.0" //for reactivemongo 1.*

// "com.weirddev" %% "scalalock-reactivemongo" % "1.0.6" //for reactivemongo 0.* ) ```

  1. wrap the block that should be synchronized across all nodes in cluster with Lock#acquire() method call

    • For scalalock-mongo (based on mongo-scala-driver)
    import com.mongodb.ConnectionString
    import com.weirddev.scalalock.mongo.MongoDistributedLock
    import org.mongodb.scala.{MongoClient, MongoClientSettings, MongoDatabase}
    import scala.concurrent.Future
    import scala.concurrent.duration._
    
    protected val db: MongoDatabase = MongoClient(MongoClientSettings.builder()
        .applyConnectionString(new ConnectionString("mongodb://localhost:27017"))
        .build()).getDatabase("test")
    
    val distLock:Lock = new MongoDistributedLock(db)
    
    distLock.acquire("some_task_id", 10 minutes){
      Future{
        println("this block needs to execute in isolation across all application nodes in cluster")
        Thread.sleep(5000)
        "Task Completed"
      }
    }
    • Alternatively, using scalalock-reactivemongo (based on org.reactivemongo)
    import com.weirddev.scalalock.reactivemongo.ReactiveMongoDistributedLock
    import reactivemongo.api.{DefaultDB, MongoConnection, MongoDriver}
    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.Future
    import scala.concurrent.duration._
    
    private val mongoUri: String = "mongodb://localhost:27017/test"
      val driver = new MongoDriver
      val database: Future[DefaultDB] = for {
        uri <- Future.fromTry(MongoConnection.parseURI(mongoUri))
        con = driver.connection(uri)
        dn <- Future(uri.db.get)
        db <- con.database(dn)
      } yield db
    
      val distLock = new ReactiveMongoDistributedLock(database)
    
    distLock.acquire("some_task_id", 10 minutes){
      Future{
        println("this block needs to execute in isolation across all application nodes in cluster")
        Thread.sleep(5000)
        "Task Completed"
      }
    }
    • A simpler alternative using scalalock-reactivemongo in Play! Framework (add a dependency on reactivemongo play, i.e. "org.reactivemongo" %% "play2-reactivemongo" % "0.16.5-play26" )
    import com.weirddev.scalalock.reactivemongo.ReactiveMongoDistributedLock
    import scala.concurrent.{ExecutionContext, Future}
    import scala.concurrent.duration._
    import javax.inject.Inject
    import play.modules.reactivemongo.ReactiveMongoApi
    
    class MyRepository @Inject()(override val reactiveMongoApi: ReactiveMongoApi)(implicit val ec: ExecutionContext){
      val distLock = new ReactiveMongoDistributedLock(reactiveMongoApi.database)
    
      distLock.acquire("some_task_id", 10 minutes){
        Future{
          println("this block needs to execute in isolation across all application nodes in cluster")
          Thread.sleep(5000)
          "Task Completed"
        }
      }
    }
  2. If you want to use the lock to synchronize task execution globally in a cluster and mandating a minimal interval - pass the releaseLockWhenDone as false. This will keep the lock state as LOCKED even after task completion. Effectively keeping the lock until expiration - as set by the expire param. example:

      val result = mongoDistLock.acquire(resourceId =  "test_task", expire = 30 minutes,releaseLockWhenDone = false){
          println("After this block finishes to execute, the lock will retain for 30 minutes before any other task using resource id - test_task - could run again")
          Thread.sleep(5000)
           "Done"
      }

    For other usage scenarios, review the integrative test code

Contributing/Developing

Welcomed :) - Please refer to CONTRIBUTING.md file.

License

Copyright (c) 2016 - 2024 WeirdDev. Licensed for free usage under the terms and conditions of Apache V2 - Apache V2 License.