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


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="idi527" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  idi527
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<blockquote>
<p>I ended up on this thread because something didn’t feel right about this decision. I didn’t know how to kill it aside from using a  <code>cron</code>  job - I need these carts to expire. So naturally I’m thinking Redis or Mnesia - but carts are kind of transient. I’m not interested in the  <code>Cart</code>  itself, and I’m only interested short-term in the “state” of the cart. A cart will either be used to execute an order in the near-term, or it won’t.</p>
</blockquote>
<p>The genserver behaviour has a timeout option which can be used to terminate the process after some idle time (no messages). Together with a dynamic supervisor and a registry it makes ephemeral processes possible.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Cart.Application do
  use Application

  def start(_type, _args) do
    children =
        [
          {Registry, keys: :unique, name: Cart.Registry},
          Cart.Supervisor
        ]

    opts = [strategy: :one_for_one, name: __MODULE__.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
</code></pre>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Cart.Supervisor do
  use DynamicSupervisor

  def start_link(opts) do
    DynamicSupervisor.start_link(__MODULE__, opts, name: __MODULE__)
  end

  @impl true
  def init(_opts) do
    DynamicSupervisor.init(strategy: :one_for_one)
  end

  @spec start_cart(cart_id :: pos_integer) :: DynamicSupervisor.on_start_child()
  @spec start_cart(cart_id :: pos_integer, opts :: Keyword.t()) :: DynamicSupervisor.on_start_child()
  def start_cart(cart_id, opts \\ []) when is_integer(cart_id) do
    DynamicSupervisor.start_child(__MODULE__, {Cart, [{:cart_id, cart_id} | opts]})
  end

  @spec stop_cart(cart_id :: pos_integer) :: :ok | {:error, :not_found}
  def stop_cart(cart_id) when is_integer(cart_id) do
    case Registry.lookup(Cart.Registry, cart_id) do
      [{pid, _}] -&gt; DynamicSupervisor.terminate_child(__MODULE__, pid)
      [] -&gt; {:error, :not_found}
    end
  end
end
</code></pre>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Cart do
  use GenServer, restart: :transient

  require Record
  Record.defrecordp(:state, [:timeout]) # I usually keep a bit more data here ;)

  @timeout 10 * 60 * 1000 # exit after 10 minutes of inactivity

  def start_link(opts) do
    cart_id = opts[:cart_id] || raise("need :cart_id")
    GenServer.start_link(__MODULE__, opts, name: via(cart_id))
  end

  def add(cart_id, item) do
    call(cart_id, {:add, item})
  end

  @doc false
  def via(cart_id) when is_integer(cart_id) do
    {:via, Registry, {Cart.Registry, cart_id}}
  end

  defp call(cart_id, message) when is_integer(cart_id) do
    GenServer.call(via(cart_id), message)
  catch
    :exit, {:noproc, _} -&gt; # a bit of a hack, but it works
      _ = Cart.Supervisor.start_cart(cart_id)
      call(cart_id, message)
  end
  # ^^^ make sure the process can always be started
  # otherwise you might get stack overflow (try/catch is not tail recursive)
  # if there is a possibility of a faulty process, add an attempt counter
  # call(cart_id, message, attempts_left - 1)

  @doc false
  def init(opts) do
    send(self(), :init)
    {:ok, state(timeout: opts[:timeout] || @timeout)} # custom timeouts for tests
  end

  @doc false
  def handle_info(:init, state(timeout: timeout) = state) do
    # init process state
    # previous state can be read from a database
    {:noreply, state, timeout}
  end

  def handle_info(:timeout, state) do
    # the process has been idle for 10 minutes, time to die
    # the current state can be persisted
    {:stop, :normal, state}
  end

  @doc false
  def handle_call({:add, item}, _from, state(timeout: timeout) = state) do
    # add item to the cart, maybe persist it in the database as well
    {:reply, :ok, state, timeout}
  end
end
</code></pre>
<p>Usage:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Cart.add(123, %Cart.Item{...}) # will start a cart process if it doesn't yet exist
Cart.add(123, %Cart.Item{...}) # uses the same process, will exit after 10 min of inactivity
</code></pre>
<p>It basically works as a very simplified version of orleans. But also inherits one of its state-managing benefits that most caches can’t provide – there are no stale entries / data races since the only way to update the cart is through interacting with a cart process. This approach also works <a href="https://github.com/erleans/erleans" rel="noopener nofollow ugc">across nodes</a>.</p>
<hr>
<blockquote>
<p>This is where the functional break happened in my mind:  <em>I’m much more interested in the data produced than the cart’s behavior</em> . Logs can tell me much more than the current state of the cart and an arbitrary status flag.</p>
</blockquote>
<p>It’s possible to persist each event in either the <code>call</code> function or in each of <code>handle_call</code>s. In chat bots where I mostly use this approach (for user sessions) I persist almost everything (but in sqlite, each process (user) gets its own database), so that I can replay the events in case of a failure / faulty migration.</p>
<p>Moving to ephemeral gen(servers | statems) from ets tables made the code much clearer as well (for me, at least). The message handlers now just call the processes and render the results. Functional core, imperative shell, and all that.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="101617" 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/discussion-about-uses-for-agent-processes/4214/72">Post #71</a>
	                </div>
	            </div>
              <div id="likers-container-101617" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="101617"
                     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 #71"></div>
  </section>
</div>
    <div class="postbit" id="101618" data-post-id="101618">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Qqwy" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Qqwy/120/1349_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Qqwy
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>TypeCheck Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Also, instead of Redis, there are multiple in-memory KV-stores that have the possibility of limit the time-to-live of their data available on Hex.PM <img src="https://forum.elixirforum.com/images/emoji/apple/smiley.png?v=15" title=":smiley:" class="emoji" alt=":smiley:" 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="101618" 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/discussion-about-uses-for-agent-processes/4214/73">Post #72</a>
	                </div>
	            </div>
              <div id="likers-container-101618" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="101618"
                     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 #72"></div>
  </section>
</div>
    <div class="postbit" id="101620" data-post-id="101620">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="OvermindDL1" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/OvermindDL1/120/2677_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  OvermindDL1
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Qqwy" data-post="73" data-topic="4214">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qqwy/48/1349_2.png" class="avatar"> Qqwy:</div>
<blockquote>
<p>Also, instead of Redis, there are multiple in-memory KV-stores that have the possibility of limit the time-to-live of their data available on Hex.PM <img src="https://forum.elixirforum.com/images/emoji/apple/smiley.png?v=15" title=":smiley:" class="emoji" alt=":smiley:" loading="lazy" width="20" height="20"> !</p>
</blockquote>
</aside>
<p>I’m quite partial to Cachex myself.  <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="101620" 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/discussion-about-uses-for-agent-processes/4214/74">Post #73</a>
	                </div>
	            </div>
              <div id="likers-container-101620" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="101620"
                     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 #73"></div>
  </section>
</div>
    <div class="postbit" id="101623" data-post-id="101623">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="robconery1" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/robconery1/120/12431_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  robconery1
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Qqwy" data-post="73" data-topic="4214">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qqwy/48/1349_2.png" class="avatar"> Qqwy:</div>
<blockquote>
<p>Also, instead of Redis, there are multiple in-memory KV-stores</p>
</blockquote>
</aside>
<p>I’m using Redis to interop (using pub/sub) with other containers written in various languages. Long story - but Redis is a great choice for this else I might have gone with ETS :).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="101623" 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/discussion-about-uses-for-agent-processes/4214/75">Post #74</a>
	                </div>
	            </div>
              <div id="likers-container-101623" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="101623"
                     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 #74"></div>
  </section>
</div>
    <div class="postbit" id="101626" data-post-id="101626">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Qqwy" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Qqwy/120/1349_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Qqwy
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>TypeCheck Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>If you already are using Redis, then of course it is a good choice <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">! What made you use Redis for that, rather than e.g. RabbitMQ?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="101626" 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/discussion-about-uses-for-agent-processes/4214/76">Post #75</a>
	                </div>
	            </div>
              <div id="likers-container-101626" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="101626"
                     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 #75"></div>
  </section>
</div>
    <div class="postbit" id="101632" data-post-id="101632">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="robconery1" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/robconery1/120/12431_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  robconery1
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I know Redis, as do my teammates. Some know Rabbit - I’ve read a few books on it and I think it will work well if we need something more than a super simple pub/sub blast.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="101632" 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/discussion-about-uses-for-agent-processes/4214/77">Post #76</a>
	                </div>
	            </div>
              <div id="likers-container-101632" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="101632"
                     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>