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
[master]: Stop Netty server reloading configuration on each request #8335
[master]: Stop Netty server reloading configuration on each request #8335
Conversation
2.6.x version of this PR: #8334. |
I couldn't work out a way to unit test configuration loading without adding intrusive instrumentation into the server code so I plan to verify this by hand before merging. 🤷♂️ |
* NOTE: This will use the ApplicationProvider of the server to get the application instance. | ||
* Use {@code Server.getHandlerFor(request, tryApp)} to pass a specific application instance | ||
*/ | ||
@deprecated("Use Server.getHandlerFor(request, tryApp) instead", "2.7.0") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of #8334 this method will be deprecated in 2.6.13. The method is highly unlikely to be used outside Play so I think it's OK to remove it for 2.7.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Anyway, maybe is worth to add a note in Migration27.md
.
request: RequestHeader, | ||
tryApp: Try[Application] | ||
): Either[Future[Result], (RequestHeader, Handler, Application)] = { | ||
): (RequestHeader, Handler) = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's easier to return a Handler
than an Either[Future[Result], Handler]
, since the returned Future[Result]
is always just turned into a Handler
anyway...
Also the returned Application
is not helpful if we're trying to preserve the reference equality of the Try[Application]
.
@@ -84,40 +82,31 @@ private[play] class PlayRequestHandler(val server: NettyServer, val serverHeader | |||
|
|||
val tryRequest: Try[RequestHeader] = modelConversion(tryApp).convertRequest(channel, request) | |||
|
|||
def clientError(statusCode: Int, message: String) = { | |||
def clientError(statusCode: Int, message: String): (RequestHeader, Handler) = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quite a lot of code in this PR now uses a (RequestHeader, Handler)
pair instead of other types. This is probably something we could represent as a special type at some point. Maybe there's a state monad in here for the RequestHeader
...
@@ -264,12 +247,10 @@ private[play] class PlayRequestHandler(val server: NettyServer, val serverHeader | |||
* Handle an essential action. | |||
*/ | |||
private def handleAction(action: EssentialAction, requestHeader: RequestHeader, | |||
request: HttpRequest, app: Option[Application]): Future[HttpResponse] = { | |||
implicit val mat: Materializer = app.fold(server.materializer)(_.materializer) | |||
request: HttpRequest, tryApp: Try[Application]): Future[HttpResponse] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is the key fix. We're ensuring we pass the original Try[Application]
through.
val unparsedTarget = modelConversion(tryApp).createUnparsedRequestTarget(request) | ||
val requestHeader = modelConversion(tryApp).createRequestHeader(channel, request, unparsedTarget) | ||
val result = errorHandler(server.applicationProvider.current).onClientError(requestHeader, statusCode, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is the only behaviour change I made in the refactoring—using tryApp
instead of applicationProvider.current
—however I think the new code is the correct behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, too.
@@ -225,25 +225,6 @@ class AkkaHttpServer( | |||
} | |||
} | |||
|
|||
private def getHandler( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
private def errorHandler(app: Option[Application]): HttpErrorHandler = | ||
app.fold[HttpErrorHandler](DefaultHttpErrorHandler)(_.errorHandler) | ||
private def errorHandler(tryApp: Try[Application]): HttpErrorHandler = | ||
tryApp.fold[HttpErrorHandler](_ => DefaultHttpErrorHandler, _.errorHandler) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The master branch build will have the same problem with Scala 2.11 (no fold for Try
).
Since we will drop Scala 2.11, we can add a comment here to use fold
when dropping Scala 2.11.
* NOTE: This will use the ApplicationProvider of the server to get the application instance. | ||
* Use {@code Server.getHandlerFor(request, tryApp)} to pass a specific application instance | ||
*/ | ||
@deprecated("Use Server.getHandlerFor(request, tryApp) instead", "2.7.0") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Anyway, maybe is worth to add a note in Migration27.md
.
Due to @pettyjamesm's report of issues with Akka HTTP in addition to known issues (#7812) with Netty I've decided to be more conservative and there is now instrumentation in the server and automated tests to check reloads. The instrumentation is enabled with a configuration setting so it can be used during testing but is disabled in production. Instrumentation details are passed as a request attribute. This is one of those times it would be nice to have JMX hooks so we can view internal server stats. Using a request attribute is the next best thing though. Reload counts now appear to be correct on both backends. |
Got this probably transient failure on one test, restarting.
|
I've run the the HelloWorld microbenchmark before and after this change and the change does seem to improve performance. |
ea3f937
to
67331f3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm except a few suggestions on the migration guide
@@ -92,3 +92,7 @@ Like announced in the [[Play 2.6 Migration Guide|Migration26#Java-Form-Changes]] | |||
### `Guava` version updated to 24.0-jre | |||
|
|||
Play 2.6.x provided 23.0 version of Guava library. Now it is updated to last actual version, 24.1-jre. Lots of changes were made in library, you can see the full changelog [here](https://github.com/google/guava/releases). | |||
|
|||
### `Server.getHandlerFor` has been removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would write Server#getHandlerFor
which is the common notation for instance methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK!
|
||
### `Server.getHandlerFor` has been removed | ||
|
||
This method on the `Server` trait was used internally by the Play server code when routing requests. It has been removed and replaced with a method of the same name on the `Server` object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should either mention that the new method is private[server]
or not mention it at all since it's not public API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would mention it. Last time I tried to figure out new relic's tracing logic, this was a "method of interest" and changing the semantics around it might have broken some visibility for a significant number of users, especially for a patch release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, fine with me, as long as we note that it's not public API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll make an "internal API changes" section in the migration documentation and we'll try to mention any notable changes here. No guarantees though because we do make quite a few internal changes and it'll be hard to track everything.
Fixes playframework#7812. The caching of the ReloadCache uses reference equality so we need to be more careful when passing around the Try[Application] returned by ApplicationProvider.get. We need to make sure we keep the same reference. Also did some refactoring of code while I was making these changes.
7537311
to
09a10f4
Compare
Migration guide updated, merging. |
Fixes #7812.
The caching of the
ReloadCache
uses reference equality so we need to be more careful when passing around the Try[Application] returned byApplicationProvider.get
. We need to make sure we keep the same reference.Also did some refactoring of code while I was making these changes.