|
|
|
|
|
by jerf
5787 days ago
|
|
'I don't know what you mean by "broken exception handling".' Take the following Python-esque (but not Python) psuedo-code in a threaded language: try:
header = read(socket, header_size)
if has_short_flag(header):
return read_until_closed(socket)
else:
handle_message(header, socket)
except SocketClosedException:
log("lost socket while processing headers in packet")
The evented equivalent of this code will have to be chopped into pieces at each of the read calls, and it is impossible to wrap the second read calls in the same exception handler like this. You can manually route exceptions around if you're careful, but that is definitely a pain in the ass and is sometimes very hard to test (hard to test exception handling for an exception you can't really fake for some reason). (Of course you can't always test it in threaded code either, but it's radically simpler there and therefore less likely to break, you don't have to test the plumbing.)This is one of the reasons I've spent the last few years fleeing event-based code towards things like Erlang, rather than running towards it; I've been doing event-based code for non-trivial work and things well beyond "demos" and the plumbing just explodes in your face if you want to build actually-robust software where simply crashing in the middle of a request isn't acceptable. Despite my best application of good coding practices and refactoring you still can't get close to the simplicity of something like Erlang code. (By the way, if you are stuck in evented land, one of the things I have learned the hard way is that anywhere your evented API has an error callback, you absolutely must provide one that does something sensible. I now always wrap such APIs in another layer of API that does nothing but crash as soon as possible if no error callback is provided. If you can't figure out what your error callback for a given such call should be, that's your design trying to tell you something's wrong.) |
|
That said, your (admittedly hypothetical) example is hideously broken. In EventMachine, and written properly:
Notice the single exception handler bracketing the entire request. (This code is wordier than I'd like because I hoisted the exception handler out of handle_next_request to illustrate it).The mistake you made (and it's common to a lot of evented code) is in driving the entire system off raw events. Don't do that. Buffer, to create natural functional decomposition.
Evented code is rarely as simple as synchronous code, but there's no reason it has to be needlessly choppy.
That said, I think this design is overvalued. Yes, it's true, you (probably) can't always wrap an entire request's processing in a single exception handler in any evented Ruby library I know about. But I wouldn't wrap an entire request handler in a single exception handler in any case! If I was preparing to deal with a database exception, I'd wrap the code making the database call in an handler for the database exception. If I was preparing for a filesystem exception, &c &c &c.
Incidentally, I've been doing very, very, very large scale evented systems for going on about 10 years now (large scale: every connection traversing tier 1 ISP backbones), and, sorry, this stuff has never blown up on me. I may have been shielded from exception drama by working in C/C++, where exceptions are not the norm. I was a thread guy before that. Threads definitely did blow up on me, a lot.