Skip to content

Instantly share code, notes, and snippets.

@mushtaq
Created March 13, 2019 13:05
Show Gist options
  • Save mushtaq/53610a1c9afec766cba0df962b79c106 to your computer and use it in GitHub Desktop.
Save mushtaq/53610a1c9afec766cba0df962b79c106 to your computer and use it in GitHub Desktop.
package com.thoughtworks
import java.util.concurrent.Executors
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
trait Tap {
def apply[A](future: Future[A]): Future[A]
}
object Tap {
type Percentage = Double
def make(
errBound: Percentage,
qualified: Throwable => Boolean,
rejected: => Throwable
): Tap = {
val singleThreadedEc = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor())
new TapImpl(errBound, qualified, rejected)(singleThreadedEc)
}
val tailSize = 100
private case class TapState(lastResults: List[Boolean]) {
def updateWith(newResult: Boolean): TapState = {
copy(lastResults = (newResult :: lastResults).take(tailSize))
}
def failed: Int = lastResults.count(_ == false)
}
private final class TapImpl(
errBound: Percentage,
qualified: Throwable => Boolean,
rejected: => Throwable
)(implicit ec: ExecutionContext)
extends Tap {
private var state = TapState(Nil)
override def apply[A](future: Future[A]): Future[A] = Future.unit.flatMap { _ =>
if (isOpen) {
future.onComplete {
case Success(_) => state = state.updateWith(true)
case Failure(e) => state = state.updateWith(qualified(e))
}
future
} else {
state = state.updateWith(true)
Future.failed(rejected)
}
}
private def isOpen: Boolean = state.failed <= state.lastResults.size * errBound
}
}
@mushtaq
Copy link
Author

mushtaq commented Mar 13, 2019

This is a mechanical translation of https://gist.github.com/danilbykov/957487c9463a66daa69290698a9320ac using Futures on singleThreaded ExecutionContext.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment