-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
[2.6.x]: Avoid multiple calls to ApplicationProvider#get for a single request #8169
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
Conversation
|
@richdougherty You might want to review this also. I had to use a different signature on |
5798578 to
f6e6b6c
Compare
| val decodedRequest = HttpRequestDecoder.decodeRequest(request) | ||
| val requestId = requestIDs.incrementAndGet() | ||
| val (convertedRequestHeader, requestBodySource) = modelConversion.convertRequest( | ||
| val tryApp = applicationProvider.get |
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 discussed, at this point you could take the logic in reloadValue(tryApp: Try[Application]): ReloadCacheValue method and change it to return a LoadedApplication, then pass that LoadedApplication around. e.g.
case class LoadedApplication(val tryApp: Try[Application], resultUtils: ServerResultUtils, modelConversion: AkkaModelConversion)Then you could pass around the LoadedApplication object instead of a Try[Application]. You could also remove the reloading/caching code, which would be nice.
If you want to keep Play 2.6 changes minimal then this is an idea to consider for the master branch.
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, I'm not sure about making major changes in 2.6, but I'm still not happy with adding API like I'm doing now. I think anything we do will require some change to the API.
The main difference is that in 2.6 we still need handleWebCommand, so we need to pass around something that has that functionality. That's why I added a method that took ApplicationProvider. I guess LoadedApplication could have handleWebCommand as well?
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, I'd say put whatever things you want into it. I'd try to keep it as private/opaque as possible so we minimise the API that is exposed.
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 think I might take a more conservative approach in 2.6 and avoid making any public API changes. Then I'll implement your ideas on 2.7. It's going to be kind of a mess to do it both places.
richdougherty
left a comment
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.
A few suggested changes, but looks good. 👍
| private def getHandler( | ||
| requestHeader: RequestHeader, tryApp: Try[Application] | ||
| ): (RequestHeader, Handler, Try[Application]) = { | ||
| Server.getHandlerFor(requestHeader, new ApplicationProvider { |
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.
What about making a new, named, local class called FixedApplicationProvider or LoadedApplicationProvider? Might make reading the code and debugging a bit clearer.
| def clientError(statusCode: Int, message: String) = { | ||
| val unparsedTarget = modelConversion.createUnparsedRequestTarget(request) | ||
| val requestHeader = modelConversion.createRequestHeader(channel, request, unparsedTarget) | ||
| val unparsedTarget = modelConversion(tryApp).createUnparsedRequestTarget(request) |
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.
If you did go with the idea of having a LoadedApplication object then this code would change to something like:
val unparsedTarget = loadedApp.modelConversion.createUnparsedRequestTarget(request)which looks a bit nicer. :)
| override def handleWebCommand(requestHeader: RequestHeader): Option[Result] = | ||
| server.applicationProvider.handleWebCommand(requestHeader) | ||
| override def get: Try[Application] = tryApp | ||
| }) match { |
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.
Again, maybe introduce a name class for this. Since it's shared between the Netty and Akka HTTP servers, it could be private[play] in the play.core.server package.
f6e6b6c to
5e3a5cd
Compare
| * - If an exception is thrown. | ||
| */ | ||
| def getHandlerFor(request: RequestHeader): Either[Future[Result], (RequestHeader, Handler, Application)] = { | ||
| private[server] def getHandlerFor( |
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.
Note I made this private[server]
Backport of #8166