-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #246 from broadinstitute/lockNew
add lock
- Loading branch information
Showing
27 changed files
with
353 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
FROM iflavoursbv/sbt-openjdk-8-alpine | ||
FROM bigtruedata/sbt | ||
|
||
COPY src /app/src | ||
COPY test.sh /app | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
sbt.version = 0.13.15 | ||
sbt.version = 1.2.6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
logLevel := Level.Warn | ||
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2") | ||
|
||
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.0.0") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
automation/src/test/scala/org/broadinstitute/dsde/test/Generators.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.broadinstitute.dsde.workbench.test | ||
|
||
import org.broadinstitute.dsde.workbench.google.{CollectionName, Document} | ||
import org.broadinstitute.dsde.workbench.google.util.LockPath | ||
import org.scalacheck.Gen | ||
import scala.concurrent.duration._ | ||
|
||
object Generators { | ||
val genLockPath: Gen[LockPath] = for { | ||
collectionName <- Gen.alphaStr.map(x => CollectionName(s"test$x")) | ||
document <- Gen.alphaStr.map(x => Document(s"test$x")) | ||
} yield LockPath(collectionName, document, 5 seconds) | ||
} |
3 changes: 2 additions & 1 deletion
3
automation/src/test/scala/org/broadinstitute/dsde/test/SamConfig.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,11 @@ | ||
package org.broadinstitute.dsde.test | ||
package org.broadinstitute.dsde.workbench.test | ||
|
||
import org.broadinstitute.dsde.workbench.config.CommonConfig | ||
|
||
object SamConfig extends CommonConfig { | ||
// from common: qaEmail, pathToQAPem | ||
object GCS extends CommonGCS { | ||
val appsDomain = gcsConfig.getString("appsDomain") | ||
val pathToSamTestFirestoreAccountPath = gcsConfig.getString("firestoreAccountPath") | ||
} | ||
} |
19 changes: 9 additions & 10 deletions
19
automation/src/test/scala/org/broadinstitute/dsde/test/api/SamApiSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
automation/src/test/scala/org/broadinstitute/dsde/test/util/DistributedLockSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
package org.broadinstitute.dsde.workbench.test.util | ||
|
||
import java.util.concurrent.TimeUnit | ||
|
||
import cats.effect.IO | ||
import cats.kernel.Eq | ||
import cats.implicits._ | ||
import org.broadinstitute.dsde.workbench.google.GoogleFirestoreOpsInterpreters | ||
import org.broadinstitute.dsde.workbench.google.util.{ | ||
DistributedLock, | ||
DistributedLockConfig | ||
} | ||
import org.broadinstitute.dsde.workbench.model.WorkbenchException | ||
import org.scalatest.{AsyncFlatSpec, Matchers} | ||
import org.broadinstitute.dsde.workbench.test.Generators.genLockPath | ||
import org.broadinstitute.dsde.workbench.test.SamConfig.GCS | ||
|
||
import scala.collection.JavaConverters._ | ||
import scala.concurrent.duration._ | ||
|
||
class DistributedLockSpec extends AsyncFlatSpec with Matchers { | ||
implicit val cs = IO.contextShift(scala.concurrent.ExecutionContext.global) | ||
implicit val timer = IO.timer(scala.concurrent.ExecutionContext.global) | ||
implicit val eqWorkbenchException: Eq[WorkbenchException] = | ||
(x: WorkbenchException, y: WorkbenchException) => | ||
x.getMessage == y.getMessage | ||
|
||
val config = DistributedLockConfig(5 seconds, 5) | ||
|
||
val lockResource: cats.effect.Resource[IO, DistributedLock[IO]] = for { | ||
db <- GoogleFirestoreOpsInterpreters.firestore[IO]( | ||
GCS.pathToSamTestFirestoreAccountPath | ||
) | ||
} yield { | ||
val googeFireStoreOps = GoogleFirestoreOpsInterpreters.ioFirestore(db) | ||
DistributedLock("samServiceTest", config, googeFireStoreOps) | ||
} | ||
|
||
"acquireLock" should "succeed if a lock can be retrieved" in { | ||
val lockPath = genLockPath.sample.get | ||
val res = lockResource.use { lock => lock.acquireLock(lockPath)} | ||
|
||
res.attempt.map(r => r.isRight shouldBe true).unsafeToFuture() | ||
} | ||
|
||
it should "fail if there's same lock has already been set within 30 seconds" in { | ||
val lockPath = genLockPath.sample.get | ||
val res = lockResource.use { lock => | ||
for { | ||
_ <- lock.acquireLock(lockPath) | ||
_ <- IO.sleep(2 seconds) | ||
failed <- lock.acquireLock(lockPath).attempt | ||
} yield { | ||
failed.swap.toOption.get.asInstanceOf[WorkbenchException].getMessage shouldBe(s"can't get lock: $lockPath") | ||
} | ||
} | ||
res.unsafeToFuture() | ||
} | ||
|
||
"releaseLock" should "remove lockPath" in { | ||
val lockPath = genLockPath.sample.get | ||
val res = lockResource.use { dl => | ||
for { | ||
lock <- dl.acquireLock(lockPath) | ||
_ <- dl.releaseLock(lockPath) | ||
released <- dl.googleFirestoreOps | ||
.get(lockPath.collectionName, lockPath.document) | ||
} yield { | ||
released.getData.asScala shouldBe (null) | ||
} | ||
} | ||
|
||
res.unsafeToFuture() | ||
} | ||
|
||
"withLock" should "eventually get a lock with max retry" in { | ||
val lockPath = genLockPath.sample.get | ||
val collectionNameWithPrefix = lockPath.collectionName.copy(asString = "samServiceTest-" + lockPath.collectionName.asString) | ||
val lockPathWithPrefix = lockPath.copy(collectionName = collectionNameWithPrefix, expiresIn = 7 seconds) //fix expiresIn so that we won't be waiting for too long in the unit test | ||
val res = lockResource.use { lock => | ||
for { | ||
current <- timer.clock.realTime(TimeUnit.MILLISECONDS) | ||
_ <- lock.acquireLock(lockPathWithPrefix) | ||
_ <- IO.sleep(2 seconds) | ||
acquireTime <- lock.withLock(lockPath).use { _ => | ||
timer.clock | ||
.realTime(TimeUnit.MILLISECONDS) | ||
} | ||
} yield { | ||
acquireTime - current should be > lockPathWithPrefix.expiresIn.toMillis | ||
} | ||
} | ||
|
||
res.unsafeToFuture() | ||
} | ||
|
||
it should "release the lock after it's used" in { | ||
val lockPath = genLockPath.sample.get.copy(expiresIn = 5 seconds) //fix expiresIn so that we won't be waiting for too long in the unit test | ||
val collectionNameWithPrefix = lockPath.collectionName.copy(asString = "samServiceTest-" + lockPath.collectionName.asString) | ||
val lockPathWithPrefix = lockPath.copy(collectionName = collectionNameWithPrefix, expiresIn = 7 seconds) //fix expiresIn so that we won't be waiting for too long in the unit test | ||
|
||
val res = lockResource.use { lock => | ||
for { | ||
currentTime <- timer.clock.monotonic(DistributedLock.EXPIRESATTIMEUNIT) | ||
lockData <- lock.withLock(lockPath).use{ | ||
_ => | ||
lock.googleFirestoreOps.get(collectionNameWithPrefix, lockPathWithPrefix.document) | ||
} | ||
released <- lock.googleFirestoreOps | ||
.get(collectionNameWithPrefix, lockPathWithPrefix.document) //withLock will prefix the lockPrefix in the path | ||
} yield { | ||
// validate we actually set and release the same lockPath | ||
lockData.getLong(DistributedLock.EXPIRESAT).longValue() should be >= (currentTime + lockPath.expiresIn.toMillis) | ||
released.getData.asScala shouldBe (null) | ||
} | ||
} | ||
|
||
res.unsafeToFuture() | ||
} | ||
|
||
it should "fail to get a lock if max retry is reached" in { | ||
val lockPath = genLockPath.sample.get | ||
val collectionNameWithPrefix = lockPath.collectionName.copy(asString = "samServiceTest-" + lockPath.collectionName.asString) | ||
val lockPathWithPrefix = lockPath.copy(collectionName = collectionNameWithPrefix, expiresIn = 10 seconds) //fix expiresIn so that we won't be waiting for too long in the unit test | ||
|
||
val config = DistributedLockConfig(1 seconds, 2) | ||
val lockResource: cats.effect.Resource[IO, DistributedLock[IO]] = for { | ||
db <- GoogleFirestoreOpsInterpreters.firestore[IO]( | ||
GCS.pathToSamTestFirestoreAccountPath | ||
) | ||
} yield { | ||
val googeFireStoreOps = GoogleFirestoreOpsInterpreters.ioFirestore(db) | ||
DistributedLock("samServiceTest", config, googeFireStoreOps) | ||
} | ||
|
||
val res = lockResource.use { lock => | ||
for { | ||
current <- timer.clock.realTime(TimeUnit.MILLISECONDS) | ||
_ <- lock.acquireLock(lockPathWithPrefix) | ||
failed <- lock | ||
.withLock(lockPath) | ||
.use(_ => IO.unit) | ||
.attempt //this will fail to aquire lock | ||
endTime <- timer.clock.realTime(TimeUnit.MILLISECONDS) | ||
} yield { | ||
failed.swap.toOption.get.asInstanceOf[WorkbenchException].getMessage should startWith (s"Reached max retry:") | ||
// validate we actually retried certain amount of time | ||
endTime - current should be > (config.maxRetry * config.retryInterval.toMillis) | ||
} | ||
} | ||
|
||
res.unsafeToFuture() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.