Merge different Resource implementation classes#7732
Conversation
1bbdbf2 to
f5b7968
Compare
richvdh
left a comment
There was a problem hiding this comment.
so... I've got one of my annoying opinions: as discussed briefly in #synapse-dev, your _AsyncResource feels like it is mixing separate concerns, and additionally it feels like the DirectServeResources are just doing a subset of what JsonResource needs to do anyway. (I've always felt like this: DirectServeResource should have been AsyncResource to my mind.
My (current) suggestion for a class heirarchy:
Resource
^
|
AsyncResource
^ ^ ^
+---/ | \------+
AsyncHtmlResource | AsyncJsonResource
| ^
CallbackResource |
^ |
| +---/
JsonServletResource
AsyncResource overrides render to look for async_render, async_render_GET, etc; it also has stub functions for _handle_error (drops the connection and logs by default?) and _handle_result (to handle a non-empty result from async_render_*: probably does nothing default, but might even raise a not-implemented exception?)
{Json,Html}Resource can provide implementations of _handle_error and _handle_result which return something to the client, taking the place of DirectServe{Json,Html}Resource.
CallbackResource has an async_render which does the stuff you currently have inside the try block of _AsyncResource (https://github.com/matrix-org/synapse/pull/7732/files#diff-c2761a2c74c6c134fb39216aef055306R229-R256).
JsonServletResource is a renaming of the current JsonResource.
(It's unclear if we need both CallbackResource and JsonServletResource: perhaps we can combine them back into a single class, thus avoiding the multiple inheritance?)
I think you can also hook whatever it is trace_servlet is supposed to be doing at AsyncResource? Though I must admit I've failed to grok the bug you're trying to fix here.
|
I think that's basically what we have today, modulo abusing the I guess ideally all I want is to separate the routing from the rendering, although this PR gets a bit unstuck trying to support HTML vs JSON. One possible way of doing this is to have an Now, the problem with that is the error handling, as now
TLDR: the current code ends up effectively calling |
There's a bunch of stuff that
I call that at least six lines :)
I'm not sure I love this plan. |
It's also annoying boilerplatey boilerplate, to get all the exception handling right. |
|
I am a bit confused how your classes are different than mine, in terms of avoiding mixing of concerns. Each layer seems to be doing pretty much the same things, just implementing things a bit differently (or having I think having the render function call out to a function that subclasses implement to handle the request is nicer than it calling a function to get the callback is a nicer way of doing things. (Though its worth noting that doing it like that means we call I don't really understand why we would implement stub functions for
I think we can make that work either way TBH.
Fair, it was an attempt to try and separate out the concerns between rendering and routing more explicitly. |
This sentence doesn't make any sense, which is a shame because on closer inspection I think it's basically the nub of the difference between our approaches. In my approach, rather than having
trace_servlet doesn't need to be implemented as a wrapper... you could do what it's doing inline instead.
fair, scratch that part of the plan; keep them as abstract functions. |
Incidentally, I think this would be better solved by using the resource tree as it is intended (ie, having individual |
|
I think it would be helpful if you could answer what you're objections are to the current approach that you're trying to fix. I do think that your |
|
Two things, basically:
|
|
Ok, thanks. I guess I just see it all as routing, but am happy to rejig. |
|
The problem with the new approach is that now we need to move the I suppose the correct, ish, way of fixing this is to have each servlet be a Thoughts on how to get out of this welcome, though at this point I might just give up on the idea of de-duplicating the code and just fix the specific case of opentracing and move on |
oh, balls.
|
|
(otherwise yes, that is a major flaw in my plan, and it's up to you whether you stick with your proposal or patch the current situation up) |
|
Aha, actually looks like we can change the operation name, which isn't possible using the standard opentracing APIs but is possible in the jaeger specific APIs. In which case we can actually just set the operation name based on the request name at the end of the rendering, rather than duplicating that logic. (What you can and can't change is quite arbitrary....) |
to confirm: this won't confuse nested spans that have come and gone by the time we finish rendering? |
I don't believe so, as they all use opaque IDs internally. Will test on jki.re though. |
23399f3 to
40b84a0
Compare
|
Ok, I've rejigged everything as discussed. Sorry that it's all in a single commit |
richvdh
left a comment
There was a problem hiding this comment.
I'm much happier with this now; thanks!
have you checked that OPTIONS works everywhere we could conceivably have broken it?
| with scope: | ||
| result = func(request, *args, **kwargs) | ||
| if opentracing is None: | ||
| yield |
There was a problem hiding this comment.
kinda wondering about the overhead here. There's a _NullContextManager in generic_worker.py which we could pull out and use...
There was a problem hiding this comment.
A single if statement per request doesn't fill me with terror, if I'm honest.
There was a problem hiding this comment.
it's not the if statement I'm worrying about, but more the infrastructure involved in instantiating a generator, constructing a GeneratorContextManager to wrap it, and then doing the callbacks.
I'm inclined to agree it's not worth lying awake at night over though.
There was a problem hiding this comment.
Ah right I see, I forgot that we'd need to generate a context manager object each time
af3a628 to
f0f2baf
Compare
Most of the |
|
@erikjohnston Looks like I bitrotted this. There are conflicts now! 😢 |
| with scope: | ||
| result = func(request, *args, **kwargs) | ||
| if opentracing is None: | ||
| yield |
There was a problem hiding this comment.
it's not the if statement I'm worrying about, but more the infrastructure involved in instantiating a generator, constructing a GeneratorContextManager to wrap it, and then doing the callbacks.
I'm inclined to agree it's not worth lying awake at night over though.
richvdh
left a comment
There was a problem hiding this comment.
couple of minor things, generally lgtm
* commit '5cdca53aa': Merge different Resource implementation classes (#7732) Fix inconsistent handling of upper and lower cases of email addresses. (#7021) Allow YAML config file to contain None (#7779) Fix a typo. Move 1.15.2 after 1.16.0rc2. 1.16.0rc2 Remove an extraneous space. Add links to the fixes. Fix tense in the release notes. Hack to add push priority to push notifications (#7765) Add early returns to `_check_for_soft_fail` (#7769) Use symbolic names for replication stream names (#7768) Type checking for `FederationHandler` (#7770) Fix new metric where we used ms instead of seconds (#7771) Fix incorrect error message when database CTYPE was set incorrectly. (#7760) Pin link in CHANGES.md Fixes to CHANGES.md
We currently have two
Resourceclasses that work in slightly different ways, this is an attempt to de-duplicate some of the code and make things more consistent (ironically by adding more classes).Concretely, this fixes a bug where
trace_servletwould trigger a"Tried to close a non-active scope!"error each time a request was processed inDirectServeResourceif opentracing is enabled. This is because thetrace_servletwrapper was added after thewrap_async_request_handler, which meant that logcontexts would be forcibly dropped before the handling intrace_servlethappened. This is handled correctly byJsonResource, so by refactoring things so that they share the same code we can fix that bug.