{"action":"created","comment":{"url":"https://api.github.com/repos/akka/akka-http/pulls/comments/191318477","pull_request_review_id":123864914,"id":191318477,"diff_hunk":"@@ -0,0 +1,294 @@\n+/*\n+ * Copyright (C) 2009-2018 Lightbend Inc. <https://www.lightbend.com>\n+ */\n+package akka.http.impl.engine.server\n+\n+import java.util.concurrent.atomic.AtomicReference\n+\n+import akka.Done\n+import akka.actor.{ ActorSystem, Terminated }\n+import akka.annotation.{ DoNotInherit, InternalApi }\n+import akka.dispatch.ExecutionContexts\n+import akka.event.LoggingAdapter\n+import akka.http.scaladsl.Http\n+import akka.http.scaladsl.Http.{ HttpServerTerminated, HttpTerminated }\n+import akka.http.scaladsl.settings.{ RoutingSettings, ServerSettings }\n+import akka.http.scaladsl.model.{ HttpRequest, HttpResponse, StatusCodes }\n+import akka.stream.scaladsl.BidiFlow\n+\n+import scala.concurrent.duration._\n+import akka.stream._\n+import akka.stream.stage._\n+import akka.util.PrettyDuration\n+\n+import scala.annotation.tailrec\n+import scala.concurrent.{ ExecutionContext, Future, Promise }\n+import scala.concurrent.duration.{ Deadline, FiniteDuration }\n+\n+/**\n+ * Used to start the termination process of an Akka HTTP server.\n+ *\n+ * TODO more details here?\n+ */\n+// \"Hasta la vista, baby.\"\n+@DoNotInherit\n+abstract class ServerTerminator {\n+  /**\n+   * Initiate the termination sequence of this server.\n+   *\n+   * TODO more docs here\n+   */\n+  def terminate(deadline: FiniteDuration)(implicit ex: ExecutionContext): Future[HttpTerminated]\n+}\n+\n+/** INTERNAL API */\n+@InternalApi\n+object MasterServerTerminator {\n+  sealed trait State\n+  final case class AliveConnectionTerminators(ts: Set[ServerTerminator]) extends State\n+  final case class Terminating(deadline: Deadline) extends State\n+}\n+\n+/** INTERNAL API: Collects signals from per-connection terminators and manages the termination process kickoff */\n+@InternalApi\n+final class MasterServerTerminator(log: LoggingAdapter) extends ServerTerminator {\n+  import MasterServerTerminator._\n+\n+  // if we get a termination signal from the outer (user-land) it has to send this signal to all existing connection\n+  // terminators such that they can initiate the termination (draining, failing) of their respective connections.\n+\n+  private val terminators = new AtomicReference[MasterServerTerminator.State](MasterServerTerminator.AliveConnectionTerminators(Set.empty))\n+  private val termination = Promise[HttpTerminated]()\n+\n+  /**\n+   * Registers an per-connection terminator.\n+   * Once the master terminator gets the termination signal,\n+   * it will delegate the signal to all registered terminators (existing connections).\n+   *\n+   * @return true if registered successfully and not terminating, false if termination in-flight\n+   *         (and the terminators `terminate()` will be invoked in that case automatically)\n+   */\n+  @tailrec def registerConnection(terminator: ServerTerminator)(implicit ec: ExecutionContext): Boolean = {\n+    terminators.get() match {\n+      case v @ AliveConnectionTerminators(ts) ⇒\n+        terminators.compareAndSet(v, v.copy(ts = ts + terminator)) ||\n+          registerConnection(terminator) // retry\n+\n+      case Terminating(deadline) ⇒\n+        terminator.terminate(deadline.timeLeft)(ec)\n+        false // termination is in progress already, we did not register but immediately issue termination\n+    }\n+  }\n+\n+  // If a connection attempts to register once termination has started, it will immediately be rejected (though\n+  // since termination also implies unbinding such new connections should not really happen).\n+  def terminate(timeout: FiniteDuration)(implicit ex: ExecutionContext): Future[HttpTerminated] = {\n+    terminators.get() match {\n+      case v @ AliveConnectionTerminators(ts) if ts.isEmpty ⇒\n+        if (terminators.compareAndSet(v, Terminating(timeout.fromNow))) {\n+          // no connections exist to be terminated, and if some arrive after us, they will see the Terminating and terminate\n+          termination.success(HttpServerTerminated)\n+          termination.future\n+        } else terminate(timeout)(ex) // retry\n+\n+      case v @ AliveConnectionTerminators(ts) ⇒\n+        if (terminators.compareAndSet(v, Terminating(timeout.fromNow))) {\n+          // cause the termination for all connections\n+          val connectionsTerminated = Future.sequence(ts.map { _.terminate(timeout) })\n+          val serverTerminated = connectionsTerminated.map(_ ⇒ HttpServerTerminated)\n+          termination.completeWith(serverTerminated)\n+          termination.future\n+        } else terminate(timeout)(ex) // retry\n+\n+      case Terminating(existingDeadline) ⇒\n+        log.warning(s\"Issued terminate($timeout) while termination is in progress already (with deadline: time left: ${PrettyDuration.format(existingDeadline.timeLeft)}\")\n+        termination.future\n+    }\n+  }\n+}\n+\n+// TODO message better?\n+final class ServerTerminationDeadlineReached() extends RuntimeException(\"Server termination deadline reached, shutting down all connections and terminating server...\")","path":"akka-http-core/src/main/scala/akka/http/impl/engine/server/ServerTerminator.scala","position":111,"original_position":111,"commit_id":"4eefb77da14a1f338812d5fed201476f8f6a7a89","original_commit_id":"4eefb77da14a1f338812d5fed201476f8f6a7a89","user":{"login":"ktoso","id":120979,"avatar_url":"https://avatars1.githubusercontent.com/u/120979?v=4","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":"message does not really matter too much i think... since we handle it internally only, will remove todo","created_at":"2018-05-29T06:51:04Z","updated_at":"2018-05-29T06:51:04Z","html_url":"https://github.com/akka/akka-http/pull/2035#discussion_r191318477","pull_request_url":"https://api.github.com/repos/akka/akka-http/pulls/2035","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/akka/akka-http/pulls/comments/191318477"},"html":{"href":"https://github.com/akka/akka-http/pull/2035#discussion_r191318477"},"pull_request":{"href":"https://api.github.com/repos/akka/akka-http/pulls/2035"}}},"pull_request":{"url":"https://api.github.com/repos/akka/akka-http/pulls/2035","id":191046497,"html_url":"https://github.com/akka/akka-http/pull/2035","diff_url":"https://github.com/akka/akka-http/pull/2035.diff","patch_url":"https://github.com/akka/akka-http/pull/2035.patch","issue_url":"https://api.github.com/repos/akka/akka-http/issues/2035","number":2035,"state":"open","locked":false,"title":"\"Graceful\" Server Termination #188","user":{"login":"ktoso","id":120979,"avatar_url":"https://avatars1.githubusercontent.com/u/120979?v=4","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":"Resolves: https://github.com/akka/akka-http/issues/188\r\nRelates to https://github.com/akka/akka/pull/18475\r\n\r\nStill some pondering to do in the terminating phase in the terminator, but generally seems correct.\r\nI also will clean up documentation, but please review already","created_at":"2018-05-29T06:44:40Z","updated_at":"2018-05-29T06:51:04Z","closed_at":null,"merged_at":null,"merge_commit_sha":"88ad915e58ecf6cb92660d2717c79acae84fe53d","assignee":null,"assignees":[],"requested_reviewers":[{"login":"raboof","id":131856,"avatar_url":"https://avatars2.githubusercontent.com/u/131856?v=4","gravatar_id":"","url":"https://api.github.com/users/raboof","html_url":"https://github.com/raboof","followers_url":"https://api.github.com/users/raboof/followers","following_url":"https://api.github.com/users/raboof/following{/other_user}","gists_url":"https://api.github.com/users/raboof/gists{/gist_id}","starred_url":"https://api.github.com/users/raboof/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/raboof/subscriptions","organizations_url":"https://api.github.com/users/raboof/orgs","repos_url":"https://api.github.com/users/raboof/repos","events_url":"https://api.github.com/users/raboof/events{/privacy}","received_events_url":"https://api.github.com/users/raboof/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[{"id":436860477,"url":"https://api.github.com/repos/akka/akka-http/labels/needs-attention","name":"needs-attention","color":"e10c02","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/akka/akka-http/pulls/2035/commits","review_comments_url":"https://api.github.com/repos/akka/akka-http/pulls/2035/comments","review_comment_url":"https://api.github.com/repos/akka/akka-http/pulls/comments{/number}","comments_url":"https://api.github.com/repos/akka/akka-http/issues/2035/comments","statuses_url":"https://api.github.com/repos/akka/akka-http/statuses/4eefb77da14a1f338812d5fed201476f8f6a7a89","head":{"label":"ktoso:wip-graceful-terminate-http-ktoso","ref":"wip-graceful-terminate-http-ktoso","sha":"4eefb77da14a1f338812d5fed201476f8f6a7a89","user":{"login":"ktoso","id":120979,"avatar_url":"https://avatars1.githubusercontent.com/u/120979?v=4","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},"repo":{"id":72620217,"name":"akka-http","full_name":"ktoso/akka-http","owner":{"login":"ktoso","id":120979,"avatar_url":"https://avatars1.githubusercontent.com/u/120979?v=4","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},"private":false,"html_url":"https://github.com/ktoso/akka-http","description":"The Streaming-first HTTP server/module of Akka","fork":true,"url":"https://api.github.com/repos/ktoso/akka-http","forks_url":"https://api.github.com/repos/ktoso/akka-http/forks","keys_url":"https://api.github.com/repos/ktoso/akka-http/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ktoso/akka-http/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ktoso/akka-http/teams","hooks_url":"https://api.github.com/repos/ktoso/akka-http/hooks","issue_events_url":"https://api.github.com/repos/ktoso/akka-http/issues/events{/number}","events_url":"https://api.github.com/repos/ktoso/akka-http/events","assignees_url":"https://api.github.com/repos/ktoso/akka-http/assignees{/user}","branches_url":"https://api.github.com/repos/ktoso/akka-http/branches{/branch}","tags_url":"https://api.github.com/repos/ktoso/akka-http/tags","blobs_url":"https://api.github.com/repos/ktoso/akka-http/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ktoso/akka-http/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ktoso/akka-http/git/refs{/sha}","trees_url":"https://api.github.com/repos/ktoso/akka-http/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ktoso/akka-http/statuses/{sha}","languages_url":"https://api.github.com/repos/ktoso/akka-http/languages","stargazers_url":"https://api.github.com/repos/ktoso/akka-http/stargazers","contributors_url":"https://api.github.com/repos/ktoso/akka-http/contributors","subscribers_url":"https://api.github.com/repos/ktoso/akka-http/subscribers","subscription_url":"https://api.github.com/repos/ktoso/akka-http/subscription","commits_url":"https://api.github.com/repos/ktoso/akka-http/commits{/sha}","git_commits_url":"https://api.github.com/repos/ktoso/akka-http/git/commits{/sha}","comments_url":"https://api.github.com/repos/ktoso/akka-http/comments{/number}","issue_comment_url":"https://api.github.com/repos/ktoso/akka-http/issues/comments{/number}","contents_url":"https://api.github.com/repos/ktoso/akka-http/contents/{+path}","compare_url":"https://api.github.com/repos/ktoso/akka-http/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ktoso/akka-http/merges","archive_url":"https://api.github.com/repos/ktoso/akka-http/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ktoso/akka-http/downloads","issues_url":"https://api.github.com/repos/ktoso/akka-http/issues{/number}","pulls_url":"https://api.github.com/repos/ktoso/akka-http/pulls{/number}","milestones_url":"https://api.github.com/repos/ktoso/akka-http/milestones{/number}","notifications_url":"https://api.github.com/repos/ktoso/akka-http/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ktoso/akka-http/labels{/name}","releases_url":"https://api.github.com/repos/ktoso/akka-http/releases{/id}","deployments_url":"https://api.github.com/repos/ktoso/akka-http/deployments","created_at":"2016-11-02T08:39:41Z","updated_at":"2018-04-09T16:42:50Z","pushed_at":"2018-05-29T06:41:57Z","git_url":"git://github.com/ktoso/akka-http.git","ssh_url":"git@github.com:ktoso/akka-http.git","clone_url":"https://github.com/ktoso/akka-http.git","svn_url":"https://github.com/ktoso/akka-http","homepage":"http://akka.io","size":19668,"stargazers_count":0,"watchers_count":0,"language":"Scala","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"akka:master","ref":"master","sha":"2c5123487a3bb36c0484e436b40b9c61ff9b3b1f","user":{"login":"akka","id":1496237,"avatar_url":"https://avatars2.githubusercontent.com/u/1496237?v=4","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":66077955,"name":"akka-http","full_name":"akka/akka-http","owner":{"login":"akka","id":1496237,"avatar_url":"https://avatars2.githubusercontent.com/u/1496237?v=4","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-http","description":"The Streaming-first HTTP server/module of Akka","fork":false,"url":"https://api.github.com/repos/akka/akka-http","forks_url":"https://api.github.com/repos/akka/akka-http/forks","keys_url":"https://api.github.com/repos/akka/akka-http/keys{/key_id}","collaborators_url":"https://api.github.com/repos/akka/akka-http/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/akka/akka-http/teams","hooks_url":"https://api.github.com/repos/akka/akka-http/hooks","issue_events_url":"https://api.github.com/repos/akka/akka-http/issues/events{/number}","events_url":"https://api.github.com/repos/akka/akka-http/events","assignees_url":"https://api.github.com/repos/akka/akka-http/assignees{/user}","branches_url":"https://api.github.com/repos/akka/akka-http/branches{/branch}","tags_url":"https://api.github.com/repos/akka/akka-http/tags","blobs_url":"https://api.github.com/repos/akka/akka-http/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/akka/akka-http/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/akka/akka-http/git/refs{/sha}","trees_url":"https://api.github.com/repos/akka/akka-http/git/trees{/sha}","statuses_url":"https://api.github.com/repos/akka/akka-http/statuses/{sha}","languages_url":"https://api.github.com/repos/akka/akka-http/languages","stargazers_url":"https://api.github.com/repos/akka/akka-http/stargazers","contributors_url":"https://api.github.com/repos/akka/akka-http/contributors","subscribers_url":"https://api.github.com/repos/akka/akka-http/subscribers","subscription_url":"https://api.github.com/repos/akka/akka-http/subscription","commits_url":"https://api.github.com/repos/akka/akka-http/commits{/sha}","git_commits_url":"https://api.github.com/repos/akka/akka-http/git/commits{/sha}","comments_url":"https://api.github.com/repos/akka/akka-http/comments{/number}","issue_comment_url":"https://api.github.com/repos/akka/akka-http/issues/comments{/number}","contents_url":"https://api.github.com/repos/akka/akka-http/contents/{+path}","compare_url":"https://api.github.com/repos/akka/akka-http/compare/{base}...{head}","merges_url":"https://api.github.com/repos/akka/akka-http/merges","archive_url":"https://api.github.com/repos/akka/akka-http/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/akka/akka-http/downloads","issues_url":"https://api.github.com/repos/akka/akka-http/issues{/number}","pulls_url":"https://api.github.com/repos/akka/akka-http/pulls{/number}","milestones_url":"https://api.github.com/repos/akka/akka-http/milestones{/number}","notifications_url":"https://api.github.com/repos/akka/akka-http/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/akka/akka-http/labels{/name}","releases_url":"https://api.github.com/repos/akka/akka-http/releases{/id}","deployments_url":"https://api.github.com/repos/akka/akka-http/deployments","created_at":"2016-08-19T11:33:36Z","updated_at":"2018-05-29T05:44:22Z","pushed_at":"2018-05-29T06:44:41Z","git_url":"git://github.com/akka/akka-http.git","ssh_url":"git@github.com:akka/akka-http.git","clone_url":"https://github.com/akka/akka-http.git","svn_url":"https://github.com/akka/akka-http","homepage":"http://akka.io","size":10172,"stargazers_count":667,"watchers_count":667,"language":"Scala","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":314,"mirror_url":null,"archived":false,"open_issues_count":473,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":314,"open_issues":473,"watchers":667,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/akka/akka-http/pulls/2035"},"html":{"href":"https://github.com/akka/akka-http/pull/2035"},"issue":{"href":"https://api.github.com/repos/akka/akka-http/issues/2035"},"comments":{"href":"https://api.github.com/repos/akka/akka-http/issues/2035/comments"},"review_comments":{"href":"https://api.github.com/repos/akka/akka-http/pulls/2035/comments"},"review_comment":{"href":"https://api.github.com/repos/akka/akka-http/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/akka/akka-http/pulls/2035/commits"},"statuses":{"href":"https://api.github.com/repos/akka/akka-http/statuses/4eefb77da14a1f338812d5fed201476f8f6a7a89"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"ktoso"}}