Skip to content

Instantly share code, notes, and snippets.

@nkallen
Created January 19, 2011 23:08
Show Gist options
  • Save nkallen/787081 to your computer and use it in GitHub Desktop.
Save nkallen/787081 to your computer and use it in GitHub Desktop.
Some scatter/gather timeout idioms
/**
* The following code represents the "best effort" idiom, where N parallel requests are made
* and for those that fail to return within a global timeout, we use a default value.
*
* Below are three implementations of the same idiom. The building blocks are CountDown latches,
* which are a kind of barrier, and Timer threads. I'm not sure which is best.
*/
"Weaver" should {
"weave" in {
var timer: JavaTimer = null
val requests = Seq(new Promise[Int], new Promise[Int], new Promise[Int])
val defaultValue = 1
doBefore { timer = new JavaTimer }
doAfter { timer.stop() }
"no timer thread and N latches" in {
val limit = 1.second.fromNow
requests.head.setValue(10) // this one request returns within time.
val sum = requests.foldLeft(0) { (sum, request) =>
// This handles all exceptions with a default value:
val value = request.get((limit - Time.now) min 0.seconds).getOrElse(defaultValue)
sum + value
}
sum mustEqual 12
}
"one timer thread and N (implicit) latches" in {
val bestEffort = requests map { request =>
request.within(timer, 1.second) handle {
case _: TimeoutException => defaultValue
}
}
requests.head.setValue(10) // this one request returns within time.
val sum = bestEffort.foldLeft(0) { (sum, requestOrDefault) =>
sum + requestOrDefault.get()
}
sum mustEqual 12
}
"uses one timer thread and one latch" in {
val latch = new CountDownLatch(requests.size)
val bestEffort = requests map { request =>
val defaultIfTimeout =
request.within(timer, 1.second) handle {
case _: TimeoutException => defaultValue
}
defaultIfTimeout respond { _ =>
latch.countDown()
}
defaultIfTimeout
}
requests.head.setValue(10)
latch.await()
val sum = bestEffort.foldLeft(0) { (sum, requestOrDefault) =>
sum + requestOrDefault.get()
}
sum mustEqual 12
}
}
}
/**
* The following code represents the "first response wins" idiom.
*/
"Weaver" should {
val requests = Seq(new Promise[Int], new Promise[Int], new Promise[Int])
"weave some more" in {
val result = new Promise[Int]
requests foreach { request =>
request respond { reply =>
result.updateIfEmpty(reply)
}
}
requests.head.setValue(1)
result.get() mustEqual 1
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment