<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="261776" data-post-id="261776">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sasajuric" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sasajuric/120/991_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sasajuric
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Author of Elixir In Action</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="dimitarvp" data-post="10" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>Not sure I follow. It’s the entire idea of a job queue having N child processes chugging away at jobs, no?</p>
</blockquote>
</aside>
<p>We have a process that wants to start the work, and the process that does the work. With <code>jobs</code> these two are actually the same. The queue process is a coordinator which accept requests and issues grants per some rule (e.g. max n at the same time, or max n per some unit of time). The client process (e.g. plug request handler) asks the queue for permission. After the permission is granted, the client proceeds, and after it’s done the client informs the queue. No other extra worker processes need to be started.</p>
<p>As a result, there might be more processes started in the system (such as plug/phoenix request handlers), but most of them will wait until the queue process gives them permission to proceed.</p>
<aside class="quote no-group" data-username="dimitarvp" data-post="10" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>Unless there are suspicions of leftover big binaries that can’t be GC-ed then IMO persistent workers are OK. But then again, we always get caught by surprise by those non-GC-able binaries so I see your point.</p>
</blockquote>
</aside>
<p>In addition, the latency of one job might be affected by the latency of previous unrelated jobs that were running in the same worker.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="9" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Although I am wondering if we shouldn’t add this to DynamicSupervisor. We already support max_children. Making it buffer would be relatively straightforward. <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
</blockquote>
</aside>
<p>Sounds good to me. However, currently if <code>max_children</code> is exceeded, <code>start_child</code> returns an error. So not sure how the API would look like for buffering.</p>
<p>Either way, I did once or twice with <a href="https://hexdocs.pm/parent/Parent.GenServer.html#content" rel="noopener nofollow ugc">Parent.GenServer</a> which is basically supervisor as a GenServer. The basic idea was to start a child when requested, unless max children are running, in which case we buffer the request. Once a child stops, we take the next req from the buffer and start the new child.</p>
<aside class="quote no-group" data-username="dimitarvp" data-post="11" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>I had one workflow where I needed two layers of workers, not one</p>
</blockquote>
</aside>
<p>As long as the worker processes don’t change the GenServer state on each request, this sounds like a scenario for <code>jobs</code> to me.</p>
<aside class="quote no-group" data-username="dimitarvp" data-post="11" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>And I’ll openly admit I am always intimidated by <code>GenStage</code>… That’s very likely a mistake.</p>
</blockquote>
</aside>
<p>I find it’s a very complex behaviour, and it definitely wouldn’t be my first choice in this scenario.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="261776" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/making-a-library-with-supervisor-genservers-cant-decide-whats-more-convenient-to-use/50325/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-261776" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="261776"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #11"></div>
  </section>
</div>
    <div class="postbit" id="261778" data-post-id="261778">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="dimitarvp" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/120/38664_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  dimitarvp
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="sasajuric" data-post="12" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sasajuric/48/991_2.png" class="avatar"> sasajuric:</div>
<blockquote>
<p>As long as the worker processes don’t change the GenServer state on each request, this sounds like a scenario for <code>jobs</code> to me.</p>
</blockquote>
</aside>
<p>It doesn’t but I really can’t see the value of <code>jobs</code> as a parallel executor in this case. The requirement isn’t “spawn minimal amount of processes” (since we all know they are very cheap), it’s more of a “make sure we don’t hit this shared resource more than necessary” or “try to limit system resources footprint while doing something that does not hit a shared resource”.</p>
<p>If I read you correctly then it sounds like <code>jobs</code> is indeed only a mini-orchestrator and permission granter: “OK, you can fetch and execute your next task now”. But doing it serially, in the same process? Sorry that I keep not getting it.</p>
<p>My usage scenario is: I want truly parallel but not <em>too</em> parallel.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="261778" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/making-a-library-with-supervisor-genservers-cant-decide-whats-more-convenient-to-use/50325/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-261778" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="261778"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #12"></div>
  </section>
</div>
    <div class="postbit" id="261808" data-post-id="261808">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="sasajuric" data-post="12" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sasajuric/48/991_2.png" class="avatar"> sasajuric:</div>
<blockquote>
<p>Sounds good to me. However, currently if <code>max_children</code> is exceeded, <code>start_child</code> returns an error. So not sure how the API would look like for buffering.</p>
</blockquote>
</aside>
<p>We can have max_children and max_buffer. Buffer by default is zero. So you still get an error if you go over the buffer value. <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="261808" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/making-a-library-with-supervisor-genservers-cant-decide-whats-more-convenient-to-use/50325/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-261808" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="261808"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #13"></div>
  </section>
</div>
    <div class="postbit" id="261809" data-post-id="261809">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sasajuric" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sasajuric/120/991_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sasajuric
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Author of Elixir In Action</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="dimitarvp" data-post="13" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>My usage scenario is: I want truly parallel but not <em>too</em> parallel.</p>
</blockquote>
</aside>
<p>Yes, this is what jobs can give you. Let’s see a few examples.</p>
<p>Suppose that in phoenix action handler you need to communicate with external service, and you want at most 5 such outbound requests to run at the same time. To do that, you need to create your queue, e.g. in app start:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">:jobs.add_queue(:my_queue, regulators: [counter: [limit: 5]])
</code></pre>
<p>Now in your phoenix action handler you can do:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">:jobs.run(
  :my_queue,
  fn -&gt;
    # communicate with remote service
  end
)
</code></pre>
<p>This will ensure that no matter how many requests are currently handled, at most 5 of them will communicate with the remote service, while the others are waiting until one of the current running <code>:jobs.run</code> invocation finishes (or crashes). The call to <code>:jobs.run</code> is blocking, and the provided lambda is running in the caller process. The result of <code>:jobs.run</code> will be the result of the provided lambda.</p>
<p>Now, let’s see another scenario. Suppose that we have a single process which fetches messages from some queue, on every message it needs to run some function, and we want to make sure that at most five of such functions are running. Here’s how we could do that:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Task.Supervisor.start_child(
  :some_supervisor,
  fn -&gt;
    :jobs.run(
      :my_queue,
      fn -&gt;
        # at most 5 of these things will run at the same time
      end
    )
  end
)
</code></pre>
<p>So in this case, we had to create a separate task per each computation, and we run the queued operation there. This is roughly similar to what you probably implemented with your abstraction, with some notable differences:</p>
<ol>
<li>Each queued operation is running in a separate one-off process</li>
<li>The user of jobs is in charge of choosing the process structure. For example, they could use <code>GenServer</code> instead of <code>Task</code> if the operation is stateful.</li>
</ol>
<p>Both of these are imo advantages of jobs over a fixed pool of long-living workers. Unless the worker has to keep some state (e.g. db connection), you don’t need such process structure. In fact, as the first example showed, you might not need to start any additional processes (other than the queue process itself, started with <code>add_queue</code>).</p>
<p>In summary, the approach taken by <code>:jobs</code> is probably the simplest one that fits the problem of running at most N things at the time. It can reduce the amount of data copying in some cases, and it is quite flexible, allowing the end user to choose their own process structure for queued operations. On top of that, jobs offer other kind of regulation strategies, such as rate limiting, or even limiting depending on the current cpu/memory usage.</p>
<p>That said, for the second example (a long-running process spawns job processes with bound concurrency), I’d probably try to use <code>Parent</code> if possible, because that would simplify the process structure, and reduce the number of processes in the system, because I wouldn’t need to start the task process until the slot in the queue is available.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="261809" data-batch-url="/posts/batch_likers">
                        5
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/making-a-library-with-supervisor-genservers-cant-decide-whats-more-convenient-to-use/50325/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-261809" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="261809"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #14"></div>
  </section>
</div>
    <div class="postbit" id="261810" data-post-id="261810">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sasajuric" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sasajuric/120/991_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sasajuric
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Author of Elixir In Action</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Ah, that’s nice, I like it <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="261810" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/making-a-library-with-supervisor-genservers-cant-decide-whats-more-convenient-to-use/50325/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-261810" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="261810"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #15"></div>
  </section>
</div>
    <div class="postbit" id="261819" data-post-id="261819">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="dimitarvp" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/120/38664_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  dimitarvp
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="sasajuric" data-post="15" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sasajuric/48/991_2.png" class="avatar"> sasajuric:</div>
<blockquote>
<p>at most 5 of them will communicate with the remote service, while the others are waiting until one of the current running <code>:jobs.run</code> invocation finishes</p>
</blockquote>
</aside>
<p>Oh. I finally get it. You meant that there might be N&gt;5 invocations of <code>:jobs.run</code> in <em>different processes</em> but 5 will be unblocked at a time. I see! Not sure why I couldn’t default to that before you spelled it out for me but now I get it. Sorry for being slow here.</p>
<aside class="quote no-group" data-username="sasajuric" data-post="15" data-topic="50325">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sasajuric/48/991_2.png" class="avatar"> sasajuric:</div>
<blockquote>
<p>Now, let’s see another scenario</p>
</blockquote>
</aside>
<p>Yes, <em>in spirit</em> (not implementation) this is more or less what I hand-rolled these years ago: a user of the library invokes <code>MyLibrary.each</code> and it then spawns X caller processes that then <code>GenServer.call</code> other Y processes (where X &gt;= Y), and those Y processes (workers) are persistent, whereas X processes (callers) are ephemeral and only spawned on demand.</p>
<hr>
<p>I don’t disagree that it’s best to give the user the building blocks and they assemble their own solution. I know a lot of former colleagues who love those libraries exclusively and ignore / look down on all others.</p>
<p>But a balanced point of view is always needed: I also consulted for and contracted for companies that needed basically the same things so my job was basically copy-paste my own mini-libraries and tweak 1-2% of the code or its config.</p>
<p>This made me wish for some pre-baked usage patterns, you know?</p>
<p>With your generous clarifications, I am now convinced I’ll use <code>:jobs</code> any chance I get because its promises to auto-adjust for system load sound amazing. Thank you! <img src="https://forum.elixirforum.com/images/emoji/apple/heart.png?v=15" title=":heart:" class="emoji" alt=":heart:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="261819" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/making-a-library-with-supervisor-genservers-cant-decide-whats-more-convenient-to-use/50325/17">Post #16</a>
	                </div>
	            </div>
              <div id="likers-container-261819" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="261819"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>