Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add websocket for hot reload #743

Merged
merged 3 commits into from
Nov 3, 2022
Merged

Add websocket for hot reload #743

merged 3 commits into from
Nov 3, 2022

Conversation

yissachar
Copy link
Collaborator

Switches from Sun to Jetty server to add websocket serve capabilities. When a Kotlin file is changed a build will be triggered, executing this task. If the server hasn't been started, we will start it. If the server is up already, then we will instead send a websocket message to all connections letting them know that an update is available and they should reload the manifest.

Use of the websocket connection is entirely optional and will just provide a slightly faster hot reload experience. Callers can still poll the manifest (and should fall back to this if the websocket connection fails).


// Keep the connection open by sending a message periodically
Timer("WebsocketHeartbeat", true).schedule(0, 10000) {
websockets.forEach { it.send(WsMessage("heartbeat")) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be a ping instead. OkHttp will actually automatically ping from the other side to determine if the connection is alive, but I suppose we don't want to rely on that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like OkHttp doesn't do this automatically but there is a setting to turn it on. The connection was closing every 30 seconds previously which is why I added the heartbeat.

I'm not sure if proper ping-pong really matters here. In the current implementation the client websocket will be closed if we don't receive a message every 30 seconds, and the server side should receive a close message most of the time when the websocket closes. There may be a slow resource leak over time where the server keeps some connections open that it shouldn't but given that this is a hot reload server we'll be restarting it constantly anyway and clearing out the old connections. That being said, I'm not particularly opposed to to ping-pong instead of heartbeat, but it's just a bit more code complication.

Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that our implementation that gives up after 30 seconds? Or is that just a default timeout?

A ping is nice because it never reaches the user code. Both clients and servers must respond automatically to pings with a pong to conform with the spec. And if it's an automatic OkHttp timeout that automatically closes the connection, the ping will keep it alive automatically.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I didn't realize that it was part of the spec, that makes more sense. Apparently http4k does not conform though. It won't respond to OkHttp's pings. Looks like it doesn't properly map frames to the backing Jetty. Womp womp.

I think the 30 seconds may be a server idle timeout, but I don't see an easy way to configure.

So we're left with the heartbeat as the simplest way to make things work, even if it's not quite the proper way of doing things. Definitely less impressed with http4k at this point, though still seems fine for our particular use case. I'll just proceed with this approach unless you have objections.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it also doesn't allow sending pings to which OkHttp would automatically reply (the spec is basically symmetric).

Definitely less impressed with http4k at this point

Yep, same.

though still seems fine for our particular use case. I'll just proceed with this approach unless you have objections.

Yep. Merge away.

@yissachar yissachar merged commit ab997c6 into trunk Nov 3, 2022
@yissachar yissachar deleted the yissachar/add-websocket branch November 3, 2022 15:06

companion object {
const val RELOAD_MESSAGE = "reload"
val websockets: MutableList<Websocket> = mutableListOf()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs synchronization for concurrent accesses.

deploymentRegistry.start(
deploymentId,
DeploymentRegistry.ChangeBehavior.BLOCK,
ZiplineServerDeploymentHandle::class.java,
server
)

// Keep the connection open by sending a message periodically
Timer("WebsocketHeartbeat", true).schedule(0, 10000) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is terse, but the right way to do this since ~2004 is ScheduledExecutorService. Or perhaps something with a coroutine.

Copy link
Collaborator

@swankjesse swankjesse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Pair("zipline", ContentType.TEXT_PLAIN)
)
)
val server = PolyHandler(http, ws).asServer(Jetty(8080))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching from SunHttp to Jetty is a bummer. You probably lose a decent amount of start-up time here.

MAYBE we finish OkHttp 5.x someday and we can use MockWebServer here, which is fast, small, and websockets-aware.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I would love to use MockWebServer, then we could get good testing too. What is the dependency on OkHttp 5? Can't we add proper WS support in 4.x?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants