3 ms·
I'm really excited about async programming in what Zed dubs the "natural style": using coroutines to suspend and resume processing at points where input is need
by sigil 14y ago
I'm really excited about async programming in what Zed dubs the "natural style": using coroutines to suspend and resume processing at points where input is needed.
Can this eliminate the callback hell that plagues the explicitly async frameworks (node.js, jquery, twisted, etc)?
But what about the downside? Now your handler is stateful and the language runtime is taking care of suspend / resume of the handler code. Can you migrate the suspended coroutine to another process, possibly on another machine? Or do you just take care to use sticky load balancing for all your stateful routes?
HN does something similar right? My understanding is that the fnid=... query param identifies a closure that's serving up a pagination of stories, and the "Unknown or expired link" error message happens when your own personal closure gets garbage collected by Arc.
In both the coroutine and closure cases, you've got something on the server side that's consuming resources even when there are no requests. This seems like a problem for scaling.
- draegtun 14y agoScaling and session affinity are the big concerns with Coroutine and Continuation based web based solutions. Sticky load balancing is the norm (http://continuity.tlt42.org/Performance_and_Scalability http://continuity.tlt42.org/Performance_and_Scalability) but it means a running Coro/Continuation can remain in memory for quite a while (Seaside saves continuation (or it did) to disk after some idle time). Still I love using these kind of libraries / frameworks especially for smaller web apps. Some refs: * See http://news.ycombinator.com/item?id=1686177 http://news.ycombinator.com/item?id=1686177 * Continuity (Perl Coro web library) - http://continuity.tlt42.org/ http://continuity.tlt42.org/ & https://metacpan.org/module/Continuity https://metacpan.org/module/Continuity * Seaside (Smalltalk Continuation based web framework) - http://www.seaside.st/ http://www.seaside.st/
- gcr 14y ago(Edit: Sorry, you seem to know all this. Keeping my post here for posterity) The logical end to this road is continuation-based web servers. See http://docs.racket-lang.org/continue/index.html http://docs.racket-lang.org/continue/index.html and my approach to the Arc challenge, https://gist.github.com/1304257 https://gist.github.com/1304257 The interesting function which does all the work is: (define (start req) ;; First, we get input from the user ;; Then, we pause for the link ;; Finally, we show what the user typed. (let ([input (get-input-from-user)]) (pause-for-link) (show-input input))) Each of these calls - get-input-from-user, pause-for-link, and show-input sends a complete page back to the user and waits for them to click a link or submit a form request. It's a really smooth way of solving some problems.
- sigil 14y agoSorry, you seem to know all this. Not at all, this is interesting. Thanks for the info.
- wetbrain 14y agoCheck out http://sheddingbikes.com/posts/1289384533.html http://sheddingbikes.com/posts/1289384533.html, >Instead of one gigantic process that has to handle all of an application, you'd have a handful of little ones. Instead of scaling massive applications across a cluster, you just scaled each interface out as needed. Instead of upgrading the whole enchilada at once, you just upgraded each interface only if it needed it. Instead of worrying about migrating user's coroutines across upgrades you just did the "Erlang trick" and moved them over to the new deployments. He goes on to describe Tir, the natural style, and Lua's coroutines. Great intro to a fun framework.
- sigil 14y agoInstead of worrying about migrating user's coroutines across upgrades you just did the "Erlang trick" and moved them over to the new deployments. I'd like to hear how this works in detail. Suppose a user is exercising a multi-request handler, say pagination of personalized results. At what point do you kill the coroutine and let the next request start a new handler in the new, upgraded process? What about state that the coroutine may have that hasn't been persisted anywhere else?
- davidhollander 14y ago> Can this eliminate the callback hell that plagues the explicitly async frameworks (node.js, jquery, twisted, etc)? But what about the downside? Can you migrate the suspended coroutine to another process No, Lua coroutines only provide language or syntax level concurrency. They make routines more stateful, which can be a benefit if you want to refer to the same local variables and scope in between yields, but do have the downside of incentivizing routines to be larger in size (although you rely on fewer of them). Also, the use case for coroutines is really limited to expressing lists of serially occurring tasks. This can work well for request-response style applications. But if for instance, you need to be able to read from, write to, and wait for timeouts on the same socket simultaneously (say a chat room), it would be a bit silly to spawn 3 coroutines per client (or 1 with heavily branching code), and use unnecessary overhead than simply binding and dispatching function calls for events without using a coroutine. Regarding dispatching function calls for events, this can also be done in a variety of ways without creating a soup of nested lambdas. For instance, local functions or object methods. This also avoids the instantiation of additional function values (lambdas) for each client, which doesn't really slow things down in Lua but uses up more memory.
- zedshaw 14y agoScaling isn't so much the problem, since you can do various things to keep users on specific servers. In my setup, I also solve a lot of the state issues since there isn't a single state for the entire app, but instead for each process that runs a small part of the app. That means you can scale and change up the application easier if you need without impacting the other parts. The real problem with coroutines for storing state is simply that upgrading the code requires you to either kill all the old state, or trickle users off on A/B paired processes. Because the state is tied to the code, you can't just change the code and hope it works. Everything will be off and the coroutines won't continue. Since Tir uses tiny processes, it's easier to do these upgrades in one part without killing the other parts, and since users are never in any one process for very long by design the risk is smaller. But, you definitely have to be more careful than the other models.
- sigil 14y ago...since users are never in any one process for very long by design the risk is smaller. You might design the interactions to be brief, but how do you actually enforce that when you need to upgrade or reclaim resources? To make this more concrete, suppose you have a shopping cart. Sure it probably only takes most people 5 minutes to check out from start to finish, but what about the stragglers? Do you have a nice way of timing out the coroutine handler due to inactivity, and saving off state when that happens? Or do you just solve it the HN way ("Unknown or expired link," hmm what does that even mean).
- j2labs 14y agoCallback hell doesn't have to exist. Python has gevent / eventlet and Node has tame.js, among others.