3 ms·
I’ve been doing some research for this earlier, and my conclusion was: This is very hard, if not impossible, to implement automatically. The main problem is tha
by judofyr 16y ago
I’ve been doing some research for this earlier, and my conclusion was: This is very hard, if not impossible, to implement automatically. The main problem is that it’s impossible to handle exceptions correctly without making the whole stack aware of it.
Currently, when an exception occurs, the system can simply change the response (since the response hasn’t been sent to the client yet, but is only buffered inside the system). With this approach, a response can be in x different states: before flushing, after the 1st flushing, … and after the xth flushing. And after the 1st flushing, the status, headers and some content has been sent to the client.
Imagine that something raises an exception after the 1st flushing. Then a 200 status has already been sent, togeher with some headers and some content. First of all, the system has to make sure the HTML is valid and at least give the user some feedback. It’s not impossible, but still a quite hard problem (because ERB doesn’t give us any hint of where tags are open/closed). The system also need to take care of all the x different state and return correct HTML in all of them.
Another issue is that we’re actually sending an error page with a 200 status. This means that the response is cacheable with whatever caching rules you decied earlier in the controller (before you knew that an error will occur). Suddenly you have your 500.html cached all over the placed, at the client-side, in your reverse proxy and everywhere.
Let’s not forget that exceptions don’t always render the error page, but do other things as well. For instance, sometimes an exception is raised to tell the system that the user needs to be authenticated or doesn’t have permission to do something. These are often implemented as Rack middlewares, but with automatic flushing they also need to take care of each x states. And if it for instance needs to redirect the user, it can’t change the status/headers to a 302/Location if it’s already in the 1st state, and therefore needs to inject a <script>window.location=’foo’</script> in a cacheable 200 response.
Of course, the views shouldn’t really raise any exceptions because it should be dumb. However, in Rails it’s very usual in Rails to defer the expensive method calls to the view. The controllers sets everything up, but it’s not until it needs to be rendered that it’s actually called. This increases the possibilty that an exception is raised in the rendering phrase.
Maybe I’m just not smart enough, but I just can’t come up with a way to tackle all of these problems (completely automated) without requiring any changes in the app.
- raggi 16y agoIt does require changes in the app, but app authors who need this kind of performance benefit will be willing to accept that hit. The solution is far better than the alternatives: - Allowing users to flush manually (people screw this up real bad) - Changing the rack spec (allowing for #each on the body to be lazily yielded, and terminating on nil or the like) - Moving to an always async stack (totally kills most users) Yes, there are plenty of issues with this, and I agree with your concern, but it is also something which can have a marked effect on performance for users. It's also worth noting that a well componentised partial can render an error in-place of the partial itself, for example, rendering a page that contains the whole layout, and a single red box of errors (say a render of the _new partial can be added to the buffer after a _create fails, instead of rendering the success box). Yes, that requires some refactoring of the application (rather than using for example, the standard 302 approach). It's also worth noting that a larger class of applications that would find this actually useful should generally have reasonable test coverage and code maturity. Whilst this isn't always the case, we also don't protect users from eval, and other evil tools, in ruby or rails.
- runeb 16y agoThat breaks caching, as pointed out by judofyr. I can imagine having to remove caching will eat up the performance gains pretty fast.
- judofyr 16y agoFrom the post: For Rails 3.1, we wanted a mostly-compatible solution with the same programmer benefits as the existing model, but with all the benefits of automatic flushing And from there he goes on with very specific implmentation details and the only caveat is some API change. This gives the impression that this is something you can easily enable for any app. I just want to point out that 100% automatic flushing is pretty much impossible with the current state of Rack/Rails, and there's still plenty of work before there's anything near flushing support in Rails. In addition, everyone should be aware of the trade-off you're making with flushing (potentially sending 500 responses as 200 Ok etc.)
- 16y ago