{"action":"created","comment":{"url":"https://api.github.com/repos/akka/akka/pulls/comments/48604079","id":48604079,"diff_hunk":"@@ -0,0 +1,247 @@\n+/**\n+ * Copyright (C) 2014-2015 Typesafe Inc. <http://www.typesafe.com>\n+ */\n+package akka.contrib.circuitbreaker\n+\n+import akka.actor._\n+import akka.event.LoggingAdapter\n+import akka.pattern._\n+import akka.util.Timeout\n+\n+import scala.concurrent.{ ExecutionContext, Future }\n+import scala.util.{ Failure, Success }\n+\n+object CircuitBreakerActor {\n+\n+  /**\n+   * Creates an circuit breaker actor proxying a target actor intended for request-reply interactions.\n+   * It is possible to send messages through this proxy without expecting a response wrapping them into a\n+   * [[akka.contrib.circuitbreaker.CircuitBreakerActor.TellOnly]]\n+   *\n+   * The circuit breaker implements the same state machine documented in [[akka.pattern.CircuitBreaker]]\n+   *\n+   * @param target the actor to proxy\n+   * @param maxFailures maximum number of failures before opening the circuit\n+   * @param callTimeout timeout before considering the ongoing call a failure\n+   * @param resetTimeout time after which the channel will be closed after entering the open state\n+   * @param circuitEventListener an actor that will receive a series of messages of type\n+   *                             [[akka.contrib.circuitbreaker.CircuitBreakerActor.CircuitBreakerEvent]]\n+   * @param failureDetector  function to detect if the a message received from the target actor as\n+   *                         response from a request represent a failure\n+   * @param failureMap   function to map a failure into a response message. The failing response message is wrapped\n+   *                     into a [[akka.contrib.circuitbreaker.CircuitBreakerActor.CircuitOpenFailure]] object\n+   */\n+  def props(target: ActorRef,\n+            maxFailures: Int,\n+            callTimeout: Timeout,\n+            resetTimeout: Timeout,\n+            circuitEventListener: Option[ActorRef],\n+            failureDetector: Any ⇒ Boolean,\n+            failureMap: CircuitOpenFailure ⇒ Any) =\n+    Props(new CircuitBreakerActor(target, maxFailures, callTimeout, resetTimeout, circuitEventListener, failureDetector, failureMap))\n+\n+  sealed trait CircuitBreakerCommand\n+\n+  final case class TellOnly(msg: Any) extends CircuitBreakerCommand\n+\n+  sealed trait CircuitBreakerResponse\n+  final case class CircuitOpenFailure(failedMsg: Any)\n+\n+  sealed trait CircuitBreakerEvent\n+  final case class CircuitOpen(circuit: ActorRef) extends CircuitBreakerCommand\n+  final case class CircuitClosed(circuit: ActorRef) extends CircuitBreakerCommand\n+  final case class CircuitHalfOpen(circuit: ActorRef) extends CircuitBreakerCommand\n+\n+  sealed trait CircuitBreakerState\n+  case object Open extends CircuitBreakerState\n+  case object Closed extends CircuitBreakerState\n+  case object HalfOpen extends CircuitBreakerState\n+\n+  final case class CircuitBreakerStateData(failureCount: Int = 0, firstHalfOpenMessageSent: Boolean = false)\n+\n+  final case class CircuitBreakerActorBuilder(\n+    maxFailures: Int, callTimeout: Timeout, resetTimeout: Timeout,\n+    circuitEventListener: Option[ActorRef] = None,\n+    failureDetector: Any ⇒ Boolean = { _ ⇒ false },\n+    openCircuitFailureConverter: CircuitOpenFailure ⇒ Any = identity) {\n+\n+    /**\n+     * Creates the props for a [[akka.contrib.circuitbreaker.CircuitBreakerActor]] proxying the given target\n+     *\n+     * @param target the target actor ref\n+     */\n+    def propsForTarget(target: ActorRef) = CircuitBreakerActor.props(target, maxFailures, callTimeout, resetTimeout, circuitEventListener, failureDetector, openCircuitFailureConverter)\n+\n+  }\n+\n+  class OpenCircuitException extends Exception(\"Circuit Open so unable to complete operation\")\n+\n+  /**\n+   * Extends [[scala.concurrent.Future]] with the method failForOpenCircuitWith to handle\n+   *   [[akka.contrib.circuitbreaker.CircuitBreakerActor.CircuitOpenFailure]] failure responses throwing\n+   *   an exception built with the given exception builder\n+   */\n+  implicit class CircuitBreakerAwareFuture(val future: Future[Any]) extends AnyVal {\n+    def failForOpenCircuit(implicit executionContext: ExecutionContext): Future[Any] = failForOpenCircuitWith(new OpenCircuitException)\n+\n+    def failForOpenCircuitWith(throwing: ⇒ Throwable)(implicit executionContext: ExecutionContext): Future[Any] = {","path":"akka-contrib/src/main/scala/akka/contrib/circuitbreaker/CircuitBreakerActor.scala","position":87,"original_position":87,"commit_id":"fd5b872a4310430802cc272f546abb8cda7121f5","original_commit_id":"fd5b872a4310430802cc272f546abb8cda7121f5","user":{"login":"ktoso","id":120979,"avatar_url":"https://avatars.githubusercontent.com/u/120979?v=3","gravatar_id":"","url":"https://api.github.com/users/ktoso","html_url":"https://github.com/ktoso","followers_url":"https://api.github.com/users/ktoso/followers","following_url":"https://api.github.com/users/ktoso/following{/other_user}","gists_url":"https://api.github.com/users/ktoso/gists{/gist_id}","starred_url":"https://api.github.com/users/ktoso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ktoso/subscriptions","organizations_url":"https://api.github.com/users/ktoso/orgs","repos_url":"https://api.github.com/users/ktoso/repos","events_url":"https://api.github.com/users/ktoso/events{/privacy}","received_events_url":"https://api.github.com/users/ktoso/received_events","type":"User","site_admin":false},"body":"`executionContext` not needed, we eagerly created failed/successful","created_at":"2015-12-30T13:40:51Z","updated_at":"2015-12-30T13:40:51Z","html_url":"https://github.com/akka/akka/pull/19239#discussion_r48604079","pull_request_url":"https://api.github.com/repos/akka/akka/pulls/19239","_links":{"self":{"href":"https://api.github.com/repos/akka/akka/pulls/comments/48604079"},"html":{"href":"https://github.com/akka/akka/pull/19239#discussion_r48604079"},"pull_request":{"href":"https://api.github.com/repos/akka/akka/pulls/19239"}}},"pull_request":{"url":"https://api.github.com/repos/akka/akka/pulls/19239","id":54179813,"html_url":"https://github.com/akka/akka/pull/19239","diff_url":"https://github.com/akka/akka/pull/19239.diff","patch_url":"https://github.com/akka/akka/pull/19239.patch","issue_url":"https://api.github.com/repos/akka/akka/issues/19239","number":19239,"state":"open","locked":false,"title":"Adding to the contrib module an Proxy Actor implementing the circuit breaker pattern","user":{"login":"galarragas","id":4570353,"avatar_url":"https://avatars.githubusercontent.com/u/4570353?v=3","gravatar_id":"","url":"https://api.github.com/users/galarragas","html_url":"https://github.com/galarragas","followers_url":"https://api.github.com/users/galarragas/followers","following_url":"https://api.github.com/users/galarragas/following{/other_user}","gists_url":"https://api.github.com/users/galarragas/gists{/gist_id}","starred_url":"https://api.github.com/users/galarragas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/galarragas/subscriptions","organizations_url":"https://api.github.com/users/galarragas/orgs","repos_url":"https://api.github.com/users/galarragas/repos","events_url":"https://api.github.com/users/galarragas/events{/privacy}","received_events_url":"https://api.github.com/users/galarragas/received_events","type":"User","site_admin":false},"body":"In some of my project I needed to use a circuit breaker for actor request-reply communication, mostly in Spray application for the connection from the REST API layer to the service actors. \r\nI re-implemented the same state machine documented in the AKKA CircuitBreaker pattern as an actor proxy with some small adaptation required by the different underlying communication mechanism. \r\n\r\nIt worked quite well for me so I think it might be useful as a shared resource.","created_at":"2015-12-19T14:44:29Z","updated_at":"2015-12-30T13:40:51Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9c1636a96bacea61cd6f507883f239a879ad14f0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/akka/akka/pulls/19239/commits","review_comments_url":"https://api.github.com/repos/akka/akka/pulls/19239/comments","review_comment_url":"https://api.github.com/repos/akka/akka/pulls/comments{/number}","comments_url":"https://api.github.com/repos/akka/akka/issues/19239/comments","statuses_url":"https://api.github.com/repos/akka/akka/statuses/fd5b872a4310430802cc272f546abb8cda7121f5","head":{"label":"galarragas:master","ref":"master","sha":"fd5b872a4310430802cc272f546abb8cda7121f5","user":{"login":"galarragas","id":4570353,"avatar_url":"https://avatars.githubusercontent.com/u/4570353?v=3","gravatar_id":"","url":"https://api.github.com/users/galarragas","html_url":"https://github.com/galarragas","followers_url":"https://api.github.com/users/galarragas/followers","following_url":"https://api.github.com/users/galarragas/following{/other_user}","gists_url":"https://api.github.com/users/galarragas/gists{/gist_id}","starred_url":"https://api.github.com/users/galarragas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/galarragas/subscriptions","organizations_url":"https://api.github.com/users/galarragas/orgs","repos_url":"https://api.github.com/users/galarragas/repos","events_url":"https://api.github.com/users/galarragas/events{/privacy}","received_events_url":"https://api.github.com/users/galarragas/received_events","type":"User","site_admin":false},"repo":{"id":48281923,"name":"akka","full_name":"galarragas/akka","owner":{"login":"galarragas","id":4570353,"avatar_url":"https://avatars.githubusercontent.com/u/4570353?v=3","gravatar_id":"","url":"https://api.github.com/users/galarragas","html_url":"https://github.com/galarragas","followers_url":"https://api.github.com/users/galarragas/followers","following_url":"https://api.github.com/users/galarragas/following{/other_user}","gists_url":"https://api.github.com/users/galarragas/gists{/gist_id}","starred_url":"https://api.github.com/users/galarragas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/galarragas/subscriptions","organizations_url":"https://api.github.com/users/galarragas/orgs","repos_url":"https://api.github.com/users/galarragas/repos","events_url":"https://api.github.com/users/galarragas/events{/privacy}","received_events_url":"https://api.github.com/users/galarragas/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/galarragas/akka","description":"Akka Project","fork":true,"url":"https://api.github.com/repos/galarragas/akka","forks_url":"https://api.github.com/repos/galarragas/akka/forks","keys_url":"https://api.github.com/repos/galarragas/akka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/galarragas/akka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/galarragas/akka/teams","hooks_url":"https://api.github.com/repos/galarragas/akka/hooks","issue_events_url":"https://api.github.com/repos/galarragas/akka/issues/events{/number}","events_url":"https://api.github.com/repos/galarragas/akka/events","assignees_url":"https://api.github.com/repos/galarragas/akka/assignees{/user}","branches_url":"https://api.github.com/repos/galarragas/akka/branches{/branch}","tags_url":"https://api.github.com/repos/galarragas/akka/tags","blobs_url":"https://api.github.com/repos/galarragas/akka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/galarragas/akka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/galarragas/akka/git/refs{/sha}","trees_url":"https://api.github.com/repos/galarragas/akka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/galarragas/akka/statuses/{sha}","languages_url":"https://api.github.com/repos/galarragas/akka/languages","stargazers_url":"https://api.github.com/repos/galarragas/akka/stargazers","contributors_url":"https://api.github.com/repos/galarragas/akka/contributors","subscribers_url":"https://api.github.com/repos/galarragas/akka/subscribers","subscription_url":"https://api.github.com/repos/galarragas/akka/subscription","commits_url":"https://api.github.com/repos/galarragas/akka/commits{/sha}","git_commits_url":"https://api.github.com/repos/galarragas/akka/git/commits{/sha}","comments_url":"https://api.github.com/repos/galarragas/akka/comments{/number}","issue_comment_url":"https://api.github.com/repos/galarragas/akka/issues/comments{/number}","contents_url":"https://api.github.com/repos/galarragas/akka/contents/{+path}","compare_url":"https://api.github.com/repos/galarragas/akka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/galarragas/akka/merges","archive_url":"https://api.github.com/repos/galarragas/akka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/galarragas/akka/downloads","issues_url":"https://api.github.com/repos/galarragas/akka/issues{/number}","pulls_url":"https://api.github.com/repos/galarragas/akka/pulls{/number}","milestones_url":"https://api.github.com/repos/galarragas/akka/milestones{/number}","notifications_url":"https://api.github.com/repos/galarragas/akka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/galarragas/akka/labels{/name}","releases_url":"https://api.github.com/repos/galarragas/akka/releases{/id}","created_at":"2015-12-19T12:52:02Z","updated_at":"2015-12-19T12:52:11Z","pushed_at":"2015-12-25T15:27:50Z","git_url":"git://github.com/galarragas/akka.git","ssh_url":"git@github.com:galarragas/akka.git","clone_url":"https://github.com/galarragas/akka.git","svn_url":"https://github.com/galarragas/akka","homepage":"http://akka.io","size":171242,"stargazers_count":0,"watchers_count":0,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"akka:master","ref":"master","sha":"f77fd82b1034d8386259d7c9049b32d4177d8775","user":{"login":"akka","id":1496237,"avatar_url":"https://avatars.githubusercontent.com/u/1496237?v=3","gravatar_id":"","url":"https://api.github.com/users/akka","html_url":"https://github.com/akka","followers_url":"https://api.github.com/users/akka/followers","following_url":"https://api.github.com/users/akka/following{/other_user}","gists_url":"https://api.github.com/users/akka/gists{/gist_id}","starred_url":"https://api.github.com/users/akka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/akka/subscriptions","organizations_url":"https://api.github.com/users/akka/orgs","repos_url":"https://api.github.com/users/akka/repos","events_url":"https://api.github.com/users/akka/events{/privacy}","received_events_url":"https://api.github.com/users/akka/received_events","type":"Organization","site_admin":false},"repo":{"id":130013,"name":"akka","full_name":"akka/akka","owner":{"login":"akka","id":1496237,"avatar_url":"https://avatars.githubusercontent.com/u/1496237?v=3","gravatar_id":"","url":"https://api.github.com/users/akka","html_url":"https://github.com/akka","followers_url":"https://api.github.com/users/akka/followers","following_url":"https://api.github.com/users/akka/following{/other_user}","gists_url":"https://api.github.com/users/akka/gists{/gist_id}","starred_url":"https://api.github.com/users/akka/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/akka/subscriptions","organizations_url":"https://api.github.com/users/akka/orgs","repos_url":"https://api.github.com/users/akka/repos","events_url":"https://api.github.com/users/akka/events{/privacy}","received_events_url":"https://api.github.com/users/akka/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/akka/akka","description":"Akka Project","fork":false,"url":"https://api.github.com/repos/akka/akka","forks_url":"https://api.github.com/repos/akka/akka/forks","keys_url":"https://api.github.com/repos/akka/akka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/akka/akka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/akka/akka/teams","hooks_url":"https://api.github.com/repos/akka/akka/hooks","issue_events_url":"https://api.github.com/repos/akka/akka/issues/events{/number}","events_url":"https://api.github.com/repos/akka/akka/events","assignees_url":"https://api.github.com/repos/akka/akka/assignees{/user}","branches_url":"https://api.github.com/repos/akka/akka/branches{/branch}","tags_url":"https://api.github.com/repos/akka/akka/tags","blobs_url":"https://api.github.com/repos/akka/akka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/akka/akka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/akka/akka/git/refs{/sha}","trees_url":"https://api.github.com/repos/akka/akka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/akka/akka/statuses/{sha}","languages_url":"https://api.github.com/repos/akka/akka/languages","stargazers_url":"https://api.github.com/repos/akka/akka/stargazers","contributors_url":"https://api.github.com/repos/akka/akka/contributors","subscribers_url":"https://api.github.com/repos/akka/akka/subscribers","subscription_url":"https://api.github.com/repos/akka/akka/subscription","commits_url":"https://api.github.com/repos/akka/akka/commits{/sha}","git_commits_url":"https://api.github.com/repos/akka/akka/git/commits{/sha}","comments_url":"https://api.github.com/repos/akka/akka/comments{/number}","issue_comment_url":"https://api.github.com/repos/akka/akka/issues/comments{/number}","contents_url":"https://api.github.com/repos/akka/akka/contents/{+path}","compare_url":"https://api.github.com/repos/akka/akka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/akka/akka/merges","archive_url":"https://api.github.com/repos/akka/akka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/akka/akka/downloads","issues_url":"https://api.github.com/repos/akka/akka/issues{/number}","pulls_url":"https://api.github.com/repos/akka/akka/pulls{/number}","milestones_url":"https://api.github.com/repos/akka/akka/milestones{/number}","notifications_url":"https://api.github.com/repos/akka/akka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/akka/akka/labels{/name}","releases_url":"https://api.github.com/repos/akka/akka/releases{/id}","created_at":"2009-02-16T12:51:54Z","updated_at":"2015-12-30T08:39:43Z","pushed_at":"2015-12-30T13:07:53Z","git_url":"git://github.com/akka/akka.git","ssh_url":"git@github.com:akka/akka.git","clone_url":"https://github.com/akka/akka.git","svn_url":"https://github.com/akka/akka","homepage":"http://akka.io","size":180081,"stargazers_count":4897,"watchers_count":4897,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1487,"mirror_url":null,"open_issues_count":794,"forks":1487,"open_issues":794,"watchers":4897,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/akka/akka/pulls/19239"},"html":{"href":"https://github.com/akka/akka/pull/19239"},"issue":{"href":"https://api.github.com/repos/akka/akka/issues/19239"},"comments":{"href":"https://api.github.com/repos/akka/akka/issues/19239/comments"},"review_comments":{"href":"https://api.github.com/repos/akka/akka/pulls/19239/comments"},"review_comment":{"href":"https://api.github.com/repos/akka/akka/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/akka/akka/pulls/19239/commits"},"statuses":{"href":"https://api.github.com/repos/akka/akka/statuses/fd5b872a4310430802cc272f546abb8cda7121f5"}}}}