mirror of
https://github.com/enso-org/enso.git
synced 2024-12-19 07:02:04 +03:00
Simplify shutdown logic on client disconnect in project-manager (#11712)
* Drop soft-shutdown on last client disconnect Suspend on Windows confuses the reconnection logic and triggers a full shutdown. This change simply drop shutdown on last client disconnect and expects and explicit command. * Various cherry-picks Minor cherry-picks from the debugging branch. Should reduce the amount of non-critical warnings.
This commit is contained in:
parent
16765455c2
commit
65010dffa7
@ -166,6 +166,9 @@ final class ContextRegistry(
|
|||||||
sender() ! AccessDenied
|
sender() ! AccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case DestroyContextResponse(_) =>
|
||||||
|
// Initiated by *this* registry. Ignore
|
||||||
|
|
||||||
case PushContextRequest(client, contextId, stackItem) =>
|
case PushContextRequest(client, contextId, stackItem) =>
|
||||||
if (store.hasContext(client.clientId, contextId)) {
|
if (store.hasContext(client.clientId, contextId)) {
|
||||||
val item = getRuntimeStackItem(stackItem)
|
val item = getRuntimeStackItem(stackItem)
|
||||||
|
@ -75,9 +75,12 @@ class JsonRpcServer(
|
|||||||
}
|
}
|
||||||
.to(
|
.to(
|
||||||
Sink.actorRef[MessageHandler.WebMessage](
|
Sink.actorRef[MessageHandler.WebMessage](
|
||||||
messageHandler,
|
messageHandler, {
|
||||||
MessageHandler.Disconnected(port),
|
logger.trace("JSON sink stream finished with no failure")
|
||||||
{ _: Throwable =>
|
MessageHandler.Disconnected(port)
|
||||||
|
},
|
||||||
|
{ e: Throwable =>
|
||||||
|
logger.trace("JSON sink stream finished with a failure", e)
|
||||||
MessageHandler.Disconnected(port)
|
MessageHandler.Disconnected(port)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -100,7 +103,7 @@ class JsonRpcServer(
|
|||||||
logger.trace(s"Sent text message ${textMessage.text}.")
|
logger.trace(s"Sent text message ${textMessage.text}.")
|
||||||
}
|
}
|
||||||
|
|
||||||
Flow.fromSinkAndSource(incomingMessages, outgoingMessages)
|
Flow.fromSinkAndSourceCoupled(incomingMessages, outgoingMessages)
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def serverRoute(port: Int): Route = {
|
override protected def serverRoute(port: Int): Route = {
|
||||||
|
@ -169,18 +169,17 @@ class LanguageServerController(
|
|||||||
* @param connectionInfo language server connection info
|
* @param connectionInfo language server connection info
|
||||||
* @param serverProcessManager an actor that manages the lifecycle of the server process
|
* @param serverProcessManager an actor that manages the lifecycle of the server process
|
||||||
* @param clients list of connected clients
|
* @param clients list of connected clients
|
||||||
* @param scheduledShutdown cancellable timeout of the hard shutdown event and a port number of the client that initiated it
|
* @param lastClientPort if no clients are connected denotes the last port number of a client or a project
|
||||||
* @return current supervising actor state
|
* @return current supervising actor state
|
||||||
*/
|
*/
|
||||||
private def supervising(
|
private def supervising(
|
||||||
connectionInfo: LanguageServerConnectionInfo,
|
connectionInfo: LanguageServerConnectionInfo,
|
||||||
serverProcessManager: ActorRef,
|
serverProcessManager: ActorRef,
|
||||||
clients: Set[UUID] = Set.empty,
|
clients: Set[UUID] = Set.empty,
|
||||||
scheduledShutdown: Option[(Cancellable, Int)] = None
|
lastClientPort: Option[Int] = None
|
||||||
): Receive =
|
): Receive =
|
||||||
LoggingReceive.withLabel("supervising") {
|
LoggingReceive.withLabel("supervising") {
|
||||||
case StartServer(clientId, _, requestedEngineVersion, _, _) =>
|
case StartServer(clientId, _, requestedEngineVersion, _, _) =>
|
||||||
scheduledShutdown.foreach(_._1.cancel())
|
|
||||||
if (requestedEngineVersion != engineVersion) {
|
if (requestedEngineVersion != engineVersion) {
|
||||||
sender() ! ServerBootFailed(
|
sender() ! ServerBootFailed(
|
||||||
new IllegalStateException(
|
new IllegalStateException(
|
||||||
@ -213,7 +212,6 @@ class LanguageServerController(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
case Terminated(_) =>
|
case Terminated(_) =>
|
||||||
scheduledShutdown.foreach(_._1.cancel())
|
|
||||||
logger.debug("Bootloader for {} terminated", project)
|
logger.debug("Bootloader for {} terminated", project)
|
||||||
|
|
||||||
case StopServer(clientId, _) =>
|
case StopServer(clientId, _) =>
|
||||||
@ -224,18 +222,16 @@ class LanguageServerController(
|
|||||||
clientId,
|
clientId,
|
||||||
Some(sender()),
|
Some(sender()),
|
||||||
explicitShutdownRequested = true,
|
explicitShutdownRequested = true,
|
||||||
None,
|
lastClientPort
|
||||||
scheduledShutdown
|
|
||||||
)
|
)
|
||||||
|
|
||||||
case ScheduledShutdown(requester) =>
|
case ScheduledShutdown(requester) =>
|
||||||
shutDownServer(requester)
|
shutDownServer(requester)
|
||||||
|
|
||||||
case LanguageServerStatusRequest =>
|
case LanguageServerStatusRequest =>
|
||||||
sender() ! LanguageServerStatus(project.id, scheduledShutdown.isDefined)
|
sender() ! LanguageServerStatus(project.id, lastClientPort.isDefined)
|
||||||
|
|
||||||
case ShutDownServer =>
|
case ShutDownServer =>
|
||||||
scheduledShutdown.foreach(_._1.cancel())
|
|
||||||
shutDownServer(None)
|
shutDownServer(None)
|
||||||
|
|
||||||
case ClientDisconnected(clientId, port) =>
|
case ClientDisconnected(clientId, port) =>
|
||||||
@ -246,13 +242,11 @@ class LanguageServerController(
|
|||||||
clientId,
|
clientId,
|
||||||
None,
|
None,
|
||||||
explicitShutdownRequested = false,
|
explicitShutdownRequested = false,
|
||||||
atPort = Some(port),
|
lastClientPort.orElse(Some(port))
|
||||||
scheduledShutdown
|
|
||||||
)
|
)
|
||||||
case ClientConnected(clientId, clientPort) =>
|
case ClientConnected(clientId, clientPort) =>
|
||||||
scheduledShutdown match {
|
lastClientPort match {
|
||||||
case Some((cancellable, port)) if clientPort == port =>
|
case Some(port) if clientPort == port =>
|
||||||
cancellable.cancel()
|
|
||||||
context.become(
|
context.become(
|
||||||
supervising(
|
supervising(
|
||||||
connectionInfo,
|
connectionInfo,
|
||||||
@ -265,7 +259,6 @@ class LanguageServerController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case RenameProject(_, namespace, oldName, newName) =>
|
case RenameProject(_, namespace, oldName, newName) =>
|
||||||
scheduledShutdown.foreach(_._1.cancel())
|
|
||||||
val socket = Socket(connectionInfo.interface, connectionInfo.rpcPort)
|
val socket = Socket(connectionInfo.interface, connectionInfo.rpcPort)
|
||||||
context.actorOf(
|
context.actorOf(
|
||||||
ProjectRenameAction
|
ProjectRenameAction
|
||||||
@ -283,7 +276,6 @@ class LanguageServerController(
|
|||||||
)
|
)
|
||||||
|
|
||||||
case ServerDied =>
|
case ServerDied =>
|
||||||
scheduledShutdown.foreach(_._1.cancel())
|
|
||||||
logger.error("Language server died [{}]", connectionInfo)
|
logger.error("Language server died [{}]", connectionInfo)
|
||||||
context.stop(self)
|
context.stop(self)
|
||||||
|
|
||||||
@ -296,36 +288,24 @@ class LanguageServerController(
|
|||||||
clientId: UUID,
|
clientId: UUID,
|
||||||
maybeRequester: Option[ActorRef],
|
maybeRequester: Option[ActorRef],
|
||||||
explicitShutdownRequested: Boolean,
|
explicitShutdownRequested: Boolean,
|
||||||
atPort: Option[Int],
|
atPort: Option[Int]
|
||||||
shutdownTimeout: Option[(Cancellable, Int)]
|
|
||||||
): Unit = {
|
): Unit = {
|
||||||
val updatedClients = clients - clientId
|
val updatedClients = clients - clientId
|
||||||
if (updatedClients.isEmpty) {
|
if (updatedClients.isEmpty) {
|
||||||
if (!explicitShutdownRequested) {
|
if (!explicitShutdownRequested) {
|
||||||
logger.debug("Delaying shutdown for project {}", project.id)
|
logger.debug(
|
||||||
val scheduledShutdown: Option[(Cancellable, Int)] =
|
"Last client disconnected for project [{}]. Awaiting re-connection or shutdown",
|
||||||
shutdownTimeout.orElse(
|
project.id
|
||||||
Some(
|
|
||||||
(
|
|
||||||
context.system.scheduler.scheduleOnce(
|
|
||||||
timeoutConfig.delayedShutdownTimeout,
|
|
||||||
self,
|
|
||||||
ScheduledShutdown(maybeRequester)
|
|
||||||
),
|
|
||||||
atPort.getOrElse(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
context.become(
|
context.become(
|
||||||
supervising(
|
supervising(
|
||||||
connectionInfo,
|
connectionInfo,
|
||||||
serverProcessManager,
|
serverProcessManager,
|
||||||
Set.empty,
|
Set.empty,
|
||||||
scheduledShutdown
|
atPort
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
shutdownTimeout.foreach(_._1.cancel())
|
|
||||||
shutDownServer(maybeRequester)
|
shutDownServer(maybeRequester)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -335,7 +315,7 @@ class LanguageServerController(
|
|||||||
connectionInfo,
|
connectionInfo,
|
||||||
serverProcessManager,
|
serverProcessManager,
|
||||||
updatedClients,
|
updatedClients,
|
||||||
shutdownTimeout
|
None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ class ProjectShutdownSpec
|
|||||||
deleteProject(projectId)(client2, implicitly[Position])
|
deleteProject(projectId)(client2, implicitly[Position])
|
||||||
}
|
}
|
||||||
|
|
||||||
"ensure language server does eventually shutdown after last client disconnects" in {
|
"ensure language server does not shutdown after last client disconnects and can re-connect" in {
|
||||||
val client = new WsTestClient(address)
|
val client = new WsTestClient(address)
|
||||||
val projectId = createProject("Foo")(client, implicitly[Position])
|
val projectId = createProject("Foo")(client, implicitly[Position])
|
||||||
val socket1 = openProject(projectId)(client, implicitly[Position])
|
val socket1 = openProject(projectId)(client, implicitly[Position])
|
||||||
@ -154,7 +154,7 @@ class ProjectShutdownSpec
|
|||||||
)
|
)
|
||||||
val client2 = new WsTestClient(address)
|
val client2 = new WsTestClient(address)
|
||||||
val socket2 = openProject(projectId)(client2, implicitly[Position])
|
val socket2 = openProject(projectId)(client2, implicitly[Position])
|
||||||
socket2 shouldNot be(socket1)
|
socket2 shouldBe socket1
|
||||||
|
|
||||||
closeProject(projectId)(client2, implicitly[Position])
|
closeProject(projectId)(client2, implicitly[Position])
|
||||||
deleteProject(projectId)(client2, implicitly[Position])
|
deleteProject(projectId)(client2, implicitly[Position])
|
||||||
|
Loading…
Reference in New Issue
Block a user