Skip to content

Instantly share code, notes, and snippets.

@Yyukan
Created March 12, 2019 07:49
Show Gist options
  • Save Yyukan/49da69b0e7cf8b97ffb5419d8140dc2b to your computer and use it in GitHub Desktop.
Save Yyukan/49da69b0e7cf8b97ffb5419d8140dc2b to your computer and use it in GitHub Desktop.
The Functional Scala Concurrency Challenge http://degoes.net/articles/zio-challenge
package net
import net.Tap.Percentage
import scalaz.zio.{Ref, UIO, ZIO}
/**
* A `Tap` adjusts the flow of tasks through
* an external service in response to observed
* failures in the service, always trying to
* maximize flow while attempting to meet the
* user-defined upper bound on failures.
*/
trait Tap[-E1, +E2] {
/**
* Sends the task through the tap. The
* returned task may fail immediately with a
* default error depending on the service
* being guarded by the tap.
*/
def apply[R, E >: E2 <: E1, A](effect: ZIO[R, E, A]): ZIO[R, E, A]
}
class SmartTap[-E1, +E2](errBound : Percentage,
qualified : E1 => Boolean,
rejected : => E2,
state: Ref[Tap.State]
) extends Tap[E1, E2] {
override def apply[R, E >: E2 <: E1, A](effect: ZIO[R, E, A]): ZIO[R, E, A] = for {
_ <- state.update(f => f.copy(total = f.total + 1))
s <- state.get
r <- if (s.failed / s.total * 100 > errBound) {
ZIO.fail(rejected)
} else {
run(effect)
}
} yield r
private def run[R, E >: E2 <: E1, A](effect: ZIO[R, E, A]): ZIO[R, E, A] = for {
f <- effect.fork
r <- f.join.catchSome {
case e if qualified(e) =>
state.update(s => s.copy(failed = s.failed + 1)) *> effect
}
} yield r
}
object Tap {
type Percentage = Int
case class State(total: Double = 0, failed: Double = 0)
/**
* Creates a tap that aims for the specified
* maximum error rate, using the specified
* function to qualify errors (unqualified
* errors are not treated as failures for
* purposes of the tap), and the specified
* default error used for rejecting tasks
* submitted to the tap.
*/
def make[E1, E2](errBound : Percentage,
qualified : E1 => Boolean,
rejected : => E2): UIO[Tap[E1, E2]] =
for {
state <- Ref.make[State](State())
} yield
new SmartTap[E1, E2](
errBound,
qualified,
rejected,
state)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment