Hi Zach, Thanks for the clarity of thought that went into this post.
Perhaps it is obvious to everyone but me, but I saw this post by Christophe Grande yesterday that appears to address these concerns: "Back-pressurized interop for core.async" https://twitter.com/cgrand/status/520566182194450432 https://gist.github.com/cgrand/767673242b7f7c27f35a I'm interested to hear if this solves your problem or is about something else. Cheers Julian On Wednesday, 8 October 2014 17:00:02 UTC+11, Zach Tellman wrote: > > The reason the thread-per-connection approach is nice is because it > correctly propagates backpressure. If we're copying data from a source to > a sink (let's say reading it in from the network and writing to a file), > it's possible that the production of data may outstrip the consumption. If > this happens, we need to make sure the producer slows down, or we risk > running out of memory. In Java, the producer is typically connected to the > consumer via a blocking queue, and if the queue fills up the producer can't > send anything more to the consumer. A Java socket is one such queue, and > if it fills up it will exert backpressure via TCP. This will work no > matter how many queues or other mechanisms separate the producer and > consumer. > > However, every attempt I've seen to marry core.async to an async network > stack has been fundamentally broken, in that it doesn't do this. Often, > they'll just use 'put!', which works fine until the channel's queue fills > up, and 1024 pending puts are accumulated, and finally the channel throws > an exception. Alternately, they'll use a blocking put on the channel, > which means that any backpressure will also extend to whatever other > connections are sharing that thread or the thread pool. Note that the > software that uses core.async in this way may work flawlessly in a wide > variety of cases, but there's still an intractable failure mode lying in > wait. > > In some cases, such as http-kit's websocket mechanism, there's no way to > even exert backpressure (you register a callback, and have no way to > indicate in your callback that you can't handle more messages). This means > that any attempt to use http-kit in conjunction with core.async will be > subtly but fundamentally broken. Arguably, even without core.async in the > equation it's broken. This is not a good state of affairs. I'll admit > that it took me a few failures in production to realize how important > correct handling of backpressure is, but this isn't something that our > ecosystem can afford to ignore, especially as Clojure is used for > larger-scale projects. > > I will note that I am working on a solution to this, in the form of the > upcoming Aleph release [1]. This will model every network connection via > streams that can trivially be converted into core.async channels [2], and > which exert backpressure over TCP wherever necessary without requiring a > thread per connection. A formal beta should be available in the near > future (it's already handling billions of requests a day in production > without issue). > > Zach > > [1] https://github.com/ztellman/aleph/tree/0.4.0 > [2] https://github.com/ztellman/manifold > > > > On Tuesday, October 7, 2014 1:36:16 PM UTC-7, adrian...@mail.yu.edu wrote: >> >> It's not about 'safety' (depending on what that means in this context), >> but as Zach pointed out, if you aren't careful about backpressure you can >> run into performance bottlenecks with unrestrained async IO operations >> because although they let you code as if you could handle an unlimited >> amount of connections, obviously that isn't true. There is only a finite >> amount of data that can be buffered in and out of any network according to >> its hardware. When you don't regulate that, your system will end up >> spending an inordinate amount of time compensating for this. You don't need >> to worry about this with "regular io" because the "thread per connection" >> abstraction effectively bounds your activity within the acceptable physical >> constraints of the server. >> >> On Tuesday, October 7, 2014 2:49:30 PM UTC-4, Brian Guthrie wrote: >>> >>> >>> On Mon, Oct 6, 2014 at 12:10 AM, <adrian...@mail.yu.edu> wrote: >>> >>>> Zach makes an excellent point; I've used AsyncSocketChannels and its >>>> irk ( >>>> http://docs.oracle.com/javase/8/docs/api/java/nio/channels/AsynchronousServerSocketChannel.html), >>>> >>>> with core.async in the past. Perhaps replacing your direct >>>> java.net.Sockets >>>> with nio classes that can be given CompletionHandlers ( >>>> http://docs.oracle.com/javase/7/docs/api/java/nio/channels/CompletionHandler.html) >>>> >>>> would be a better fit. >>>> >>> >>> Once I do some performance instrumentation I'll give that a shot. I >>> admit that I'm not familiar with all the implications of using the nio >>> classes; were I to switch, is it safe to continue using go blocks, or is it >>> worth explicitly allocating a single thread per socket? >>> >>> Brian >>> >> -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.