<div dir="auto">There is an important difference between synchronous API with semaphores, blocking queues, etc and reactive API with backpressure.<div dir="auto"><br></div><div dir="auto">The difference is that reactive API is essentially continuation passing, one important aspect of which is the inversion of control. ("Don't call us, we'll call you") Whereas there is a known isomorphism between the two models of computation, the complexity of expression differs. My stock example where continuation passing is much simpler is the tree iterator vs the generator for a tree.</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">Alex</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, 21 Jun 2022, 14:11 Brian Goetz, <<a href="mailto:brian.goetz@oracle.com">brian.goetz@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

  
  <div>
    <font size="4"><font face="monospace">"Backpressure" is just a fancy
        new term for the age-old concept of bounding resource
        utilization by stalling or refusing excessive requests. 
        Examples include: <br>
        <br>
         - thread pools -- limit the number of concurrently executing
        tasks<br>
         - semaphores -- limit the number of a critical resource (socket
        connections, open files, etc), by stalling incremental requests
        until the resource becomes free<br>
         - producer/consumer with blocking queues -- when consumers are
        overloaded, stall the producers<br>
         - queuing of socket connect requests in the OS, and refusing
        additional requests after the queue gets too long<br>
         - various networking flow control protocols using send credits,
        sliding windows, etc (xmodem, TCP)<br>
        <br>
        These are often used in conjunction; a thread pool may have a
        fixed number of threads *and* a bounded queue for waiting
        tasks.  OSes queue socket requests until some limit, and then
        refuse incremental requests.  These are all techniques of slowly
        pushing the load back to the source.  <br>
        <br>
        But, backpressure is not magic; it still requires analysis and
        control.  Before you can use it effectively, you have to
        identify what the resources are that might get over-consumed,
        and choose a strategy for managing it.  Obvious strategies
        include "don't call me, I'll call you", queue size limits, etc. 
        But none of these are applied magically; they require you to
        configure them.  Reactive's contribution, such as it is, is to
        put these concepts in the foreground, where users are reminded
        to think about them.  <br>
        <br>
        All of these techniques are still available to us.  But we have
        to identify what resources are in danger of being overconsumed,
        and protect them appropriately.  The concepts for doing so are
        older then Java -- semaphores, blocking queues, etc.  <br>
      </font></font><br>
    <div>On 6/20/2022 2:33 PM,
      <a href="mailto:eric@kolotyluk.net" target="_blank" rel="noreferrer">eric@kolotyluk.net</a> wrote:<br>
    </div>
    <blockquote type="cite">
      
      
      
      <div>
        <p class="MsoNormal"><span lang="EN-US">After tinkering with
            loom and learning a lot of revised synchronous style
            practices, I was recently watching another presentation on
            Reactive Programming, and it got me thinking about how some
            of the asynchronous practices, such as Backpressure, could
            be expressed in the synchronous world of Virtual Threads,
            Structured Concurrency, etc.<u></u><u></u></span></p>
        <p class="MsoNormal"><span lang="EN-US"><u></u> <u></u></span></p>
        <p class="MsoNormal"><span lang="EN-US">After working on Akka
            Scala for years, using Reactive Practices, I had a sense
            that it might be possible to build applications/services
            that would not thrash. They would go up to 100% utilization,
            without thrashing, and then just refuse more work. <i>Sorry
              mate, I won’t do that now, maybe talk to the Load Balancer
              about spawning some more siblings…</i> I don’t know how
            true this sense is, only that it’s a hopeful sense.<u></u><u></u></span></p>
        <p class="MsoNormal"><span lang="EN-US"><u></u> <u></u></span></p>
        <p class="MsoNormal"><span lang="EN-US">While I have dabbled
            with </span>java.util.concurrent.Flow using Virtual Threads
          successfully, I still find the cognitive load for using Rx
          APIs higher than I would like, but it is well disciplined and
          has many other benefits, such as backpressure.<u></u><u></u></p>
        <p class="MsoNormal"><u></u> <u></u></p>
        <p class="MsoNormal">In the future, can we build simpler
          synchronous APIs with the benefits of asynchronous APIs such
          as Rx, leveraging the scalability/throughput of Virtual
          Threads and the discipline of Structure Concurrency? I guess I
          am just lazy, and I don’t want to think harder than I have to.<u></u><u></u></p>
        <p class="MsoNormal"><u></u> <u></u></p>
        <p class="MsoNormal">Cheers, Eric<span lang="EN-US"><u></u><u></u></span></p>
      </div>
    </blockquote>
    <br>
  </div>

</blockquote></div>