<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tracey Onim</title>
    <description>The latest articles on DEV Community by Tracey Onim (@traceyonim).</description>
    <link>https://dev.to/traceyonim</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F884510%2Fc6421302-acef-426c-96ab-e3d5f90ac87c.jpeg</url>
      <title>DEV Community: Tracey Onim</title>
      <link>https://dev.to/traceyonim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/traceyonim"/>
    <language>en</language>
    <item>
      <title>Leverage Concurrency Efficiently When Managing Multiple Tasks in Elixir</title>
      <dc:creator>Tracey Onim</dc:creator>
      <pubDate>Tue, 09 Jul 2024 10:53:15 +0000</pubDate>
      <link>https://dev.to/appsignal/leverage-concurrency-efficiently-when-managing-multiple-tasks-in-elixir-5g4b</link>
      <guid>https://dev.to/appsignal/leverage-concurrency-efficiently-when-managing-multiple-tasks-in-elixir-5g4b</guid>
      <description>&lt;p&gt;When working on multiple tasks, it's important to consider performing them concurrently. However, when using concurrency, we need to be careful not to overload our system resources.&lt;/p&gt;

&lt;p&gt;In this article, we will cover the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is concurrency?&lt;/li&gt;
&lt;li&gt;How can we use concurrency in Elixir?&lt;/li&gt;
&lt;li&gt;Common issues when working with a series of tasks concurrently.&lt;/li&gt;
&lt;li&gt;How to efficiently manage a series of tasks using the Task module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get going!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Concurrency?
&lt;/h2&gt;

&lt;p&gt;Concurrency is the ability to execute multiple tasks at the same time.&lt;/p&gt;

&lt;p&gt;Concurrency and parallelism are crucial features in modern programming. The operating systems installed on our computers are designed to incorporate these features. Both concurrency and parallelism involve executing multiple tasks simultaneously. However, concurrency focuses on managing multiple tasks within one resource. This can be achieved through techniques like multitasking and asynchronous programming. In an operating system, concurrency can be achieved by opening multiple applications to perform different tasks at the same time, also referred to as multitasking.&lt;/p&gt;

&lt;p&gt;On the other hand, parallelism involves executing multiple tasks simultaneously across multiple processing units (such as multiple CPU cores or distributed computing resources), thus improving system speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Can We Use Concurrency in Elixir?
&lt;/h2&gt;

&lt;p&gt;To achieve concurrency in Elixir, you need to initiate a process and execute the function or code responsible for performing a specific task within that process.&lt;/p&gt;

&lt;p&gt;Here's an example of some basic Elixir code that we can take a look at.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
  &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we've added a 3-second sleep to simulate the fact that sending a message to an external phone number can sometimes cause some latency.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;send_message/1&lt;/code&gt; is invoked, this function will delay processing for 3 seconds before printing a message. It will also return an &lt;code&gt;{:ok, "sent"}&lt;/code&gt; tuple to indicate that the message was successfully sent to the given phone number.&lt;/p&gt;

&lt;p&gt;When executing synchronous Elixir code like the code above, we must wait for it to complete before receiving the output. If we need to send messages to multiple phone numbers, each with a delay of 3 seconds, it could take a significant amount of time to receive a notification that all messages have been successfully sent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Taking It One Step Further
&lt;/h3&gt;

&lt;p&gt;Now, let's take it a step further and write some code that sends a message to multiple phone numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;send_to_all/1&lt;/code&gt; function takes a list of phone numbers as an argument and uses the &lt;code&gt;Enum.each/2&lt;/code&gt; function to iterate over the list, sending a message to each phone number using the &lt;code&gt;send_message&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;If you run this function in &lt;code&gt;iex&lt;/code&gt;, you will notice two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It takes longer to receive the final output, which is an atom &lt;code&gt;:ok&lt;/code&gt;. &lt;code&gt;Enum.each/2&lt;/code&gt; returns &lt;code&gt;:ok&lt;/code&gt; when invoked.&lt;/li&gt;
&lt;li&gt;Printing happens one at a time, with each print taking 3 seconds. Depending on the number of phone numbers provided, &lt;code&gt;send_message/1&lt;/code&gt; is called 5 times in our case, taking about 15 seconds to complete.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means that when sending a message to a given phone number, each phone number has to wait for the preceding phone number to execute before moving on to the next one. The execution is synchronous, also known as blocking code.&lt;/p&gt;

&lt;p&gt;Elixir provides the ability to run code concurrently or asynchronously. Asynchronous code allows you to execute multiple tasks simultaneously. This type of code is also referred to as non-blocking code because it does not hinder the primary execution of a program.&lt;/p&gt;

&lt;p&gt;Let's improve the &lt;code&gt;send_to_all/1&lt;/code&gt; function by running the task of sending messages to each phone number asynchronously. We need to start a process for each task.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A &lt;code&gt;process&lt;/code&gt; is a separate entity where code execution occurs.&lt;/em&gt; These processes are lightweight, run concurrently, and are isolated from each other. The code we write runs inside processes. The IEx (interactive shell) is an example of an Elixir process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we've used &lt;code&gt;Task.start/1&lt;/code&gt; (which we'll go over in more detail later) to start a process for each task.&lt;br&gt;
After we recompile and run this function again in our IEx, you will notice two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It runs faster than before because it's now running asynchronously. The final output — &lt;code&gt;:ok&lt;/code&gt; — is immediately returned even before we see the printed success messages.&lt;/li&gt;
&lt;li&gt;It instantly prints the success message at once for all tasks, which means that all the messages are being sent at the same time.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Running Tasks Concurrently using the Task Module
&lt;/h2&gt;

&lt;p&gt;Elixir has a standard library Task module commonly used for running code concurrently. This allows you to perform tasks asynchronously, which can lead to improved performance and responsiveness in your applications.&lt;/p&gt;

&lt;p&gt;Let's go back to our &lt;code&gt;send_to_all/1&lt;/code&gt; function. At first, we used this function to send messages to multiple phone numbers sequentially.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Sequential version of send_to_all/1&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the sequential version, &lt;code&gt;send_message/1&lt;/code&gt; was called for each message in the list, one after the other.&lt;/p&gt;

&lt;p&gt;Later on, we converted the sequential code into concurrent code using &lt;code&gt;Task.start/1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Asynchronous version using Task.start/1&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the asynchronous version, &lt;code&gt;Task.start/1&lt;/code&gt; creates a separate task for each message, allowing the tasks to execute concurrently.&lt;/p&gt;

&lt;p&gt;The Task module has several useful functions to run code concurrently. Let's now look at a few of these functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Task.start/1&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Task.start/1&lt;/code&gt; is used to start a new process/task. It takes in an anonymous function with zero arity as an argument, where the intended work is performed. Usually, it doesn't return the result of the executed function. Instead, it returns an &lt;code&gt;{:ok, PID}&lt;/code&gt; tuple, where &lt;code&gt;:ok&lt;/code&gt; means the process was started successfully, and &lt;code&gt;PID&lt;/code&gt; stands for process identifier: a number that uniquely identifies an Elixir process.&lt;/p&gt;

&lt;p&gt;Let's try it out in IEx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;:ok, pid&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; Task.start&lt;span class="o"&gt;(&lt;/span&gt;fn -&amp;gt;  IO.puts&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"converting to asyncronous code"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; end&lt;span class="o"&gt;)&lt;/span&gt;
         converting to asyncronous code
          &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="c"&gt;#PID&amp;lt;0.153.0&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message is printed instantly and the result is &lt;code&gt;{:ok, #PID&amp;lt;0.153.0&amp;gt;}&lt;/code&gt;. This means that the process was started successfully. &lt;code&gt;pid&lt;/code&gt; represents the process started by &lt;code&gt;Task.start/1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, it's hard to know if all the messages were sent successfully using &lt;code&gt;Task.start/1&lt;/code&gt;, since it will still return &lt;code&gt;{:ok, PID}&lt;/code&gt;, which stands for a process that's started successfully.&lt;/p&gt;

&lt;p&gt;It is useful in cases where you don't have interest in the returned result or whether it completes successfully.&lt;/p&gt;

&lt;p&gt;A good example is working on a background job processing system, such as sending messages to registered users every midnight. You can use &lt;code&gt;Task.start/1&lt;/code&gt; to asynchronously handle each task that sends messages to users. In this context, you're not particularly interested in the return value of &lt;code&gt;Task.start/1&lt;/code&gt;, because your main concern is to handle the tasks asynchronously, and you have a mechanism to track the status of each task. This mechanism can involve saving the status in the database, ensuring that any pending tasks are retried in subsequent runs.&lt;/p&gt;

&lt;p&gt;In case something goes wrong when sending messages to users, the status of the message will still be pending in the database. You can guarantee that the worker will run again at midnight and retry for pending messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Task.async/1&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Like &lt;code&gt;Task.start/1&lt;/code&gt;, &lt;code&gt;Task.async/1&lt;/code&gt; is also used to start a process. The difference between &lt;code&gt;Task.async/&lt;/code&gt; and &lt;code&gt;Task.start/&lt;/code&gt; is that with &lt;code&gt;Task.async/1&lt;/code&gt;, you can retrieve the function result executed within the process.&lt;br&gt;
It takes in an anonymous function with zero arity which starts a process and then returns a &lt;code&gt;%Task{}&lt;/code&gt; struct.&lt;/p&gt;

&lt;p&gt;Let's try it out in IEx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Task.async&lt;span class="o"&gt;(&lt;/span&gt;fn -&amp;gt; IO.puts&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"converting to asyncronous code using async"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; end&lt;span class="o"&gt;)&lt;/span&gt;
    converting to asyncronous code using async
      %Task&lt;span class="o"&gt;{&lt;/span&gt;
        mfa: &lt;span class="o"&gt;{&lt;/span&gt;:erlang, :apply, 2&lt;span class="o"&gt;}&lt;/span&gt;,
        owner: &lt;span class="c"&gt;#PID&amp;lt;0.109.0&amp;gt;,&lt;/span&gt;
        pid: &lt;span class="c"&gt;#PID&amp;lt;0.110.0&amp;gt;,&lt;/span&gt;
        ref: &lt;span class="c"&gt;#Reference&amp;lt;0.0.13955.2933390707.3162308609.257437&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message is printed immediately and returns a &lt;code&gt;%Task{}&lt;/code&gt; struct as the output. The returned Task struct includes useful information about the process that's started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;owner&lt;/code&gt; represents the Process Identifier (PID) of the process that started the Task. In our example, the owner is IEx since we are running code within the shell. If we run &lt;code&gt;self()&lt;/code&gt;, the function that returns the &lt;code&gt;PID&lt;/code&gt; of the current process, then the PID returned by &lt;code&gt;self()&lt;/code&gt; will indeed match the PID of the owner of the task started.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pid&lt;/code&gt;: The process identifier of the process started by the task. You can use this PID to monitor the process further if needed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ref&lt;/code&gt;: This is a process monitor reference that can be used in cases where you want to be notified about how and where a process exits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To retrieve a result from the returned task, use either &lt;a href="https://hexdocs.pm/elixir/1.13/Task.html#await/2" rel="noopener noreferrer"&gt;&lt;code&gt;Task.await/2&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://hexdocs.pm/elixir/1.13/Task.html#yield/2" rel="noopener noreferrer"&gt;&lt;code&gt;Task.yield/2&lt;/code&gt;&lt;/a&gt;. They both accept a &lt;code&gt;%Task{}&lt;/code&gt; struct as an argument and handle process timeout. However, they handle the timeout differently.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Task.await/2&lt;/code&gt; and &lt;code&gt;Task.yield/2&lt;/code&gt; have a default timeout of &lt;code&gt;5000ms&lt;/code&gt; or &lt;code&gt;5 seconds&lt;/code&gt; to ensure that processes don't get stuck waiting for a result forever. &lt;code&gt;Task.await/2&lt;/code&gt; will cause an exception and crash the process if there is a slower task taking more than &lt;code&gt;5000ms&lt;/code&gt; to complete, while &lt;code&gt;Task.yield/2&lt;/code&gt; returns nil.&lt;/p&gt;

&lt;p&gt;Let's modify &lt;code&gt;send_to_all/1&lt;/code&gt; to use &lt;code&gt;Task.async/1&lt;/code&gt;, then retrieve its result using &lt;code&gt;Task.await/2&lt;/code&gt;. Later, use &lt;code&gt;Task.yield/2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;label:&lt;/span&gt; &lt;span class="s2"&gt;"started tasks++++"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;await&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;send_to_all/1&lt;/code&gt; iterates over a given list of phone numbers using &lt;code&gt;Enum.map/2&lt;/code&gt; instead of &lt;code&gt;Enum.each/2&lt;/code&gt; to retrieve the result of each task, and spawns a task for each using &lt;code&gt;Task.async/1&lt;/code&gt;. Then we use &lt;code&gt;Task.await/2&lt;/code&gt; to retrieve the result of each task. It waits for the task to complete and then returns a result.&lt;/p&gt;

&lt;p&gt;Run the &lt;code&gt;send_to_all/1&lt;/code&gt; function in IEx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; send_to_all&lt;span class="o"&gt;(&lt;/span&gt;1..10&lt;span class="o"&gt;)&lt;/span&gt;
started tasks++++[
  %Task&lt;span class="o"&gt;{&lt;/span&gt;
    mfa: &lt;span class="o"&gt;{&lt;/span&gt;:erlang, :apply, 2&lt;span class="o"&gt;}&lt;/span&gt;,
    owner: &lt;span class="c"&gt;#PID&amp;lt;0.150.0&amp;gt;,&lt;/span&gt;
    pid: &lt;span class="c"&gt;#PID&amp;lt;0.151.0&amp;gt;,&lt;/span&gt;
    ref: &lt;span class="c"&gt;#Reference&amp;lt;0.0.19203.3913757788.1002242054.113465&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  %Task&lt;span class="o"&gt;{&lt;/span&gt;
    mfa: &lt;span class="o"&gt;{&lt;/span&gt;:erlang, :apply, 2&lt;span class="o"&gt;}&lt;/span&gt;,
    owner: &lt;span class="c"&gt;#PID&amp;lt;0.150.0&amp;gt;,&lt;/span&gt;
    pid: &lt;span class="c"&gt;#PID&amp;lt;0.152.0&amp;gt;,&lt;/span&gt;
    ref: &lt;span class="c"&gt;#Reference&amp;lt;0.0.19203.3913757788.1002242054.113469&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,..
&lt;span class="o"&gt;]&lt;/span&gt;

sending message  to 1
sending message  to 2
sending message  to 3
sending message  to 4
sending message  to 5
sending message  to 6
sending message  to 7
sending message  to 8
sending message  to 9
sending message  to 10

&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
...
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at the inspected result, each task represents a spawned process. They all have a different &lt;code&gt;pid&lt;/code&gt; with a similar &lt;code&gt;owner&lt;/code&gt; pid meaning that the caller process (IEx) is one. Once each task is completed, the messages are printed at the same time and the result returned. The final result returned is a list of output expected from &lt;code&gt;send_message/1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's retrieve the result from each spawned task using &lt;code&gt;Task.yield/2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;label:&lt;/span&gt; &lt;span class="s2"&gt;"started tasks++++"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; send_to_all&lt;span class="o"&gt;(&lt;/span&gt;1..10&lt;span class="o"&gt;)&lt;/span&gt;
started tasks++++[
  %Task&lt;span class="o"&gt;{&lt;/span&gt;
    mfa: &lt;span class="o"&gt;{&lt;/span&gt;:erlang, :apply, 2&lt;span class="o"&gt;}&lt;/span&gt;,
    owner: &lt;span class="c"&gt;#PID&amp;lt;0.150.0&amp;gt;,&lt;/span&gt;
    pid: &lt;span class="c"&gt;#PID&amp;lt;0.151.0&amp;gt;,&lt;/span&gt;
    ref: &lt;span class="c"&gt;#Reference&amp;lt;0.0.19203.3913757788.1002242054.113465&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  %Task&lt;span class="o"&gt;{&lt;/span&gt;
    mfa: &lt;span class="o"&gt;{&lt;/span&gt;:erlang, :apply, 2&lt;span class="o"&gt;}&lt;/span&gt;,
    owner: &lt;span class="c"&gt;#PID&amp;lt;0.150.0&amp;gt;,&lt;/span&gt;
    pid: &lt;span class="c"&gt;#PID&amp;lt;0.152.0&amp;gt;,&lt;/span&gt;
    ref: &lt;span class="c"&gt;#Reference&amp;lt;0.0.19203.3913757788.1002242054.113469&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,..
&lt;span class="o"&gt;]&lt;/span&gt;

sending message  to 1
sending message  to 2
sending message  to 3
sending message  to 4
sending message  to 5
sending message  to 6
sending message  to 7
sending message  to 8
sending message  to 9
sending message  to 10

&lt;span class="o"&gt;[&lt;/span&gt;
  :ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
  :ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
...
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is similar. However, &lt;code&gt;Task.yield/2&lt;/code&gt; returns an &lt;code&gt;{:ok, term()}&lt;/code&gt; tuple, where &lt;code&gt;term&lt;/code&gt; represents the result returned by the function executed within the process. That's why our output is a list of the &lt;code&gt;{:ok, {:ok, "sent"}}&lt;/code&gt; tuple where &lt;code&gt;{:ok, "sent"}&lt;/code&gt; is the expected result from &lt;code&gt;send_message/1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;send_to_all/1&lt;/code&gt; function, we used &lt;code&gt;Task.start/2&lt;/code&gt; and &lt;code&gt;Task.async/2&lt;/code&gt; to start each task of sending a message to a given phone number in the background, allowing them to run concurrently. Starting a separate process for each task when sending a message ensures its isolation. Therefore, if one task encounters an error or fails for any reason, it won't affect the execution of other tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Example with an Error
&lt;/h3&gt;

&lt;p&gt;For example, let's assume we have introduced an error in one of the tasks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
  &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
  &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Asynchronous version using Task.start/1&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the &lt;code&gt;send_message/1&lt;/code&gt; function has two function heads. The first function head pattern matches on 3 and deliberately raises an arithmetic error by adding the number 3 to a string &lt;code&gt;"a"&lt;/code&gt;. The second function head handles other &lt;code&gt;phone numbers&lt;/code&gt; values and simulates sending messages as before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; send_to_all&lt;span class="o"&gt;(&lt;/span&gt;1..5&lt;span class="o"&gt;)&lt;/span&gt;
06:19:17.562 &lt;span class="o"&gt;[&lt;/span&gt;error] Task &lt;span class="c"&gt;#PID&amp;lt;0.294.0&amp;gt; started from #PID&amp;lt;0.291.0&amp;gt; terminating&lt;/span&gt;
&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;ArithmeticError&lt;span class="o"&gt;)&lt;/span&gt; bad argument &lt;span class="k"&gt;in &lt;/span&gt;arithmetic expression
    :erlang.+&lt;span class="o"&gt;(&lt;/span&gt;3, &lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;
  ok: &lt;span class="c"&gt;#PID&amp;lt;0.292.0&amp;gt;,&lt;/span&gt;
  ok: &lt;span class="c"&gt;#PID&amp;lt;0.293.0&amp;gt;,&lt;/span&gt;
  ok: &lt;span class="c"&gt;#PID&amp;lt;0.294.0&amp;gt;,&lt;/span&gt;
  ok: &lt;span class="c"&gt;#PID&amp;lt;0.295.0&amp;gt;,&lt;/span&gt;
  ok: &lt;span class="c"&gt;#PID&amp;lt;0.296.0&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
sending message  to 1
sending message  to 2
sending message  to 4
sending message  to 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you pass a list of phone numbers to &lt;code&gt;send_to_all/1&lt;/code&gt; and one of them is 3, the corresponding task will raise an arithmetic error, but other tasks will continue to run. From the results, we can see that we are notified of an arithmetic error in one of the started processes (&lt;code&gt;#PID&amp;lt;0.294.0&amp;gt;&lt;/code&gt;). We also receive printed messages indicating that the messages were sent successfully to numbers 1, 2, 4, and 5. That means that the task started for number 3 encountered an error, but didn't prevent other processes from performing their tasks.&lt;/p&gt;

&lt;p&gt;This approach enhances concurrency by allowing tasks to run concurrently, while also ensuring that they are isolated processes. This helps to create a more robust system.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Problem: Working with a Series of Tasks
&lt;/h2&gt;

&lt;p&gt;We've seen how we have been able to send messages to the given phone numbers concurrently using &lt;a href="https://hexdocs.pm/elixir/1.13/Task.html#async/1" rel="noopener noreferrer"&gt;&lt;code&gt;Task.async/1&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://hexdocs.pm/elixir/1.13/Task.html#start/1" rel="noopener noreferrer"&gt;&lt;code&gt;Task.start/1&lt;/code&gt;&lt;/a&gt;. In the examples provided, &lt;code&gt;send_to_all/1&lt;/code&gt; spawned a process for each task when sending a message. Even though our aim is to leverage concurrency and build a fault-tolerant and robust application, we should also take into consideration that processes don't share memory.&lt;/p&gt;

&lt;p&gt;The more we increase phone numbers, the more we start a high number of concurrent tasks, each one occupying their own memory. Therefore, increasing the number of concurrent tasks can lead to an application consuming a significant amount of memory. That, in turn, can cause slow application performance and a spike in system usage, degrading system performance and maybe even making other services unresponsive.&lt;/p&gt;

&lt;p&gt;Let's make &lt;code&gt;send_to_all/1&lt;/code&gt; send messages to 1 million phone numbers. But first, we'll modify it by renaming &lt;code&gt;send_to_all/1&lt;/code&gt; to differentiate the &lt;code&gt;send_to_all/1&lt;/code&gt; using &lt;code&gt;Task.async/1&lt;/code&gt; from the one using &lt;code&gt;Task.start/1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Sender&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Asynchronous version using Task.start/1&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Asynchronous version using Task.async/1&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this modification, &lt;code&gt;send_to_all_async/1&lt;/code&gt; and &lt;code&gt;send_to_all_start/1&lt;/code&gt; functions have been defined to differentiate the behavior of &lt;code&gt;Task.async/1&lt;/code&gt; and &lt;code&gt;Task.start/1&lt;/code&gt; when attempting to run a million processes.&lt;/p&gt;

&lt;p&gt;Now, open IEx (or recompile it using the &lt;code&gt;recompile()&lt;/code&gt; command if it was already running) and call &lt;code&gt;send_to_all_start/1&lt;/code&gt; with a million phone numbers. We're not using a real phone number, therefore we simply pass a &lt;code&gt;1..1_000_000&lt;/code&gt; range of numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Sender.send_to_all_start&lt;span class="o"&gt;(&lt;/span&gt;1..1_000_000&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;06:12:14.067 &lt;span class="o"&gt;[&lt;/span&gt;error] Too many processes
&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;SystemLimitError&lt;span class="o"&gt;)&lt;/span&gt; a system limit has been reached
    :erlang.spawn&lt;span class="o"&gt;(&lt;/span&gt;Task.Supervised, :noreply, &lt;span class="o"&gt;[{&lt;/span&gt;:nonode@nohost, &lt;span class="c"&gt;#PID&amp;lt;0.150.0&amp;gt;, #PID&amp;lt;0.150.0&amp;gt;}, [#PID&amp;lt;0.150.0&amp;gt;, #PID&amp;lt;0.142.0&amp;gt;], [#PID&amp;lt;0.150.0&amp;gt;], {:erlang, :apply, [#Function&amp;lt;7.61290973/0 in Sender.send_to_all_start/1&amp;gt;, []]}])&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;elixir 1.15.5&lt;span class="o"&gt;)&lt;/span&gt; lib/task/supervised.ex:6: Task.Supervised.start/3
    &lt;span class="o"&gt;(&lt;/span&gt;elixir 1.15.5&lt;span class="o"&gt;)&lt;/span&gt; lib/enum.ex:4356: Enum.map_range/4
    &lt;span class="o"&gt;(&lt;/span&gt;elixir 1.15.5&lt;span class="o"&gt;)&lt;/span&gt; lib/enum.ex:4356: Enum.map_range/4
    &lt;span class="o"&gt;(&lt;/span&gt;elixir 1.15.5&lt;span class="o"&gt;)&lt;/span&gt; lib/enum.ex:4356: Enum.map/2
    iex:2: &lt;span class="o"&gt;(&lt;/span&gt;file&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing we can see is an error message notifying us that too many processes have been started, exceeding the system limit. This is happening as soon as &lt;code&gt;Task.start/1&lt;/code&gt; is invoked for each phone number in the range from 1 to 1,000,000. Success messages have been printed, but they are not showing for all of the phone numbers. The highest number of messages that I can see in the output is &lt;code&gt;262073&lt;/code&gt;. Since the numbers are not sorted, it is easy to miss the highest number. However, the total number of messages sent is around &lt;code&gt;262000&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Going On Here and How Do We Fix It?
&lt;/h3&gt;

&lt;p&gt;We are having issues with starting a million processes because the number of tasks spawned by &lt;code&gt;Task.start/1&lt;/code&gt; exceeds the default limit of processes (262144, for performance and memory-saving reasons) that can be started in BEAM. However, we can override this limit using &lt;code&gt;+P NUM&lt;/code&gt;. This means we can increase the limit by running the command &lt;code&gt;iex --erl '+P 1000000'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To apply this change, close IEx and restart it by running the command &lt;code&gt;iex --erl '+P 1000000' -S mix&lt;/code&gt;. Then, you can call &lt;code&gt;send_to_all_start/1&lt;/code&gt; again, with a range of phone numbers from 1 to &lt;code&gt;1000000&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sender tracey&lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;--erl&lt;/span&gt; &lt;span class="s1"&gt;'+P 1000000'&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; mix
iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Sender.send_to_all_start&lt;span class="o"&gt;(&lt;/span&gt;1..1_000_000&lt;span class="o"&gt;)&lt;/span&gt;
 :ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, the function &lt;code&gt;Enum.each/2&lt;/code&gt; has returned the final output &lt;code&gt;:ok&lt;/code&gt;. Additionally, success messages have been printed for all of the one million phone numbers. It is good that we can override the default system limit, but we must be cautious when increasing the limit, as it can cause the VM to use more memory, potentially leading to performance issues.&lt;/p&gt;

&lt;p&gt;To monitor memory usage before and after a system limit increase, use the &lt;code&gt;:observer.start&lt;/code&gt; command in IEx to open the Observer tool. The Observer shows a spike in memory usage when the system limit is increased.&lt;/p&gt;

&lt;p&gt;Before the system limit increase:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lGxv-vZu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/before-limit-inc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lGxv-vZu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/before-limit-inc.png" alt="before limit increase" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the system limit increase:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qsCSDiJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/after-limit-inc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qsCSDiJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/after-limit-inc.png" alt="after limit increase" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending Messages To the Phone Numbers with &lt;code&gt;Task.async/1&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Next, we will use the &lt;code&gt;send_to_all_async/1&lt;/code&gt; function to send messages to a million phone numbers. This function uses &lt;code&gt;Task.async/1&lt;/code&gt; to start a process for each task.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Sender.send_to_all_async&lt;span class="o"&gt;(&lt;/span&gt;1..1_000_000&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;Task.async/1&lt;/code&gt;, you can send a message to all the phone numbers. However, this capability may not be available on all machines, so keep that in mind.&lt;/p&gt;

&lt;p&gt;The Observer shows a spike in memory usage when using &lt;code&gt;Task.async/1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DyE6pfCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/with-task-async.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DyE6pfCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/with-task-async.png" alt="with task async" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running these tasks synchronously works out better in terms of memory usage compared to running them asynchronously when using a combination of &lt;a href="https://hexdocs.pm/elixir/1.12/Enum.html" rel="noopener noreferrer"&gt;&lt;code&gt;Enum&lt;/code&gt;&lt;/a&gt; with either &lt;code&gt;Task.async/1&lt;/code&gt; or &lt;code&gt;Task.start/1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ch06zG7N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/running-synchronously.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ch06zG7N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/running-synchronously.png" alt="running synchronously" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When using &lt;code&gt;Task.start/&lt;/code&gt; or &lt;code&gt;Task.async/1&lt;/code&gt; to start a process for each task, you're essentially creating a separate process for each message you want to send. While this can be an efficient way to handle concurrency, it can also lead to increased memory usage, especially when dealing with a large number of tasks simultaneously.&lt;/p&gt;

&lt;p&gt;The spike in memory usage we're seeing in the Observer is likely due to the fact that each process created by &lt;code&gt;Task.async/1&lt;/code&gt; and &lt;code&gt;Task.start/1&lt;/code&gt; consumes memory. With a million phone numbers, you're creating a million processes, which can quickly exhaust available memory, especially if each process is doing significant work or holding onto a large amount of data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Use &lt;code&gt;Task.async_stream/3&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When utilizing Task processes, the goal is to achieve concurrency to efficiently send messages to designated phone numbers. However, it is important to be mindful of system resources and avoid sudden increases in pressure.&lt;/p&gt;

&lt;p&gt;Fortunately, the Task module provides a useful feature known as &lt;a href="https://hexdocs.pm/elixir/1.13/Task.html#async_stream/3" rel="noopener noreferrer"&gt;&lt;code&gt;Task.async_stream/3&lt;/code&gt;&lt;/a&gt;. It works similarly to &lt;code&gt;Enum.map/2&lt;/code&gt; and &lt;code&gt;Task.async/2&lt;/code&gt; combined, as it creates task processes from a given list of items.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;Task.async_stream/3&lt;/code&gt;, you can perform the task for each item in the list concurrently, by starting a process. The only difference is that you can set a limit on the number of processes running at the same time with this function.&lt;/p&gt;

&lt;p&gt;For instance, suppose the message application needs to send messages to 100 phone numbers using &lt;code&gt;Task.async_stream/3&lt;/code&gt;. In this case, we can set the concurrency limit to 5, which means that, at most, only 5 processes will start to send messages to the given phone numbers at the same time.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Task.async_stream/3&lt;/code&gt; takes in three arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enumerable: This argument represents the collection of items that you want to process concurrently. It can be an &lt;a href="https://hexdocs.pm/elixir/1.12/Enum.html" rel="noopener noreferrer"&gt;Enum&lt;/a&gt; or a &lt;a href="https://hexdocs.pm/elixir/1.12/Stream.html" rel="noopener noreferrer"&gt;Stream&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Anonymous function: This must take a single argument that represents an element of the enumerable. The function is applied to each enumerable element. It defines the task to be executed concurrently for each item.&lt;/li&gt;
&lt;li&gt;Options, used to control the level of concurrency, the time tasks are allowed to run, ordering of the results and the action to take when a task times out.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;Task.async_stream/3&lt;/code&gt; returns a stream, which is a lazy enumerable. Therefore, transformations or computations on the stream are not performed as soon as the stream is created. Instead, they are performed until the stream is explicitly consumed or operated upon.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Example Using &lt;code&gt;Task.async_stream/3&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Here's an example of using &lt;code&gt;Task.async_stream/3&lt;/code&gt; in IEx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Task.async_stream&lt;span class="o"&gt;(&lt;/span&gt;1..5, fn phone_number -&amp;gt; send_message&lt;span class="o"&gt;(&lt;/span&gt;phone_number&lt;span class="o"&gt;)&lt;/span&gt; end&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;#Function&amp;lt;3.112246596/2 in Task.build_stream/3&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result that's returned is a &lt;code&gt;stream&lt;/code&gt;. To run this stream, we can use &lt;a href="https://hexdocs.pm/elixir/Stream.html#run/1" rel="noopener noreferrer"&gt;&lt;code&gt;Stream.run/1&lt;/code&gt;&lt;/a&gt;, which returns &lt;code&gt;:ok&lt;/code&gt; and isn't useful when we're not interested in the final result. Another alternative is to use the Enum functions. This is useful when you intend to perform other tasks with the expected result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 1..5 |&amp;gt; Task.async_stream&lt;span class="o"&gt;(&lt;/span&gt;fn phone_number -&amp;gt; send_message&lt;span class="o"&gt;(&lt;/span&gt;phone_number&lt;span class="o"&gt;)&lt;/span&gt; end&lt;span class="o"&gt;)&lt;/span&gt; |&amp;gt; Stream.run&lt;span class="o"&gt;()&lt;/span&gt;
sending message  to 1
sending message  to 2
sending message  to 3
sending message  to 4
sending message  to 5
:ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Stream.run/1&lt;/code&gt; is used to run the stream without collecting the result. The messages are sent to the given phone numbers and once all tasks are completed, &lt;code&gt;:ok&lt;/code&gt; is returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 1..5 |&amp;gt; Task.async_stream&lt;span class="o"&gt;(&lt;/span&gt;fn phone_number -&amp;gt; send_message&lt;span class="o"&gt;(&lt;/span&gt;phone_number&lt;span class="o"&gt;)&lt;/span&gt; end&lt;span class="o"&gt;)&lt;/span&gt; |&amp;gt; Enum.to_list
sending message  to 1
sending message  to 2
sending message  to 3
sending message  to 4
sending message  to 5

&lt;span class="o"&gt;[&lt;/span&gt;
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Enum.html#to_list/1" rel="noopener noreferrer"&gt;&lt;code&gt;Enum.to_list/1&lt;/code&gt;&lt;/a&gt; runs the stream and collects the results into a list.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Task.async_stream/3&lt;/code&gt; accepts a list of options. When not explicitly passed, the options default to their default values.&lt;br&gt;
Let's dive further into the options passed to &lt;code&gt;Task.async_stream/3&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;:max_concurrency&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;:max_concurrency&lt;/code&gt; is responsible for setting a limit on the number of processes running at the same time. Its value defaults to the number of logical cores available in a system.&lt;/p&gt;

&lt;p&gt;When running &lt;code&gt;Task.async_stream/3&lt;/code&gt; in IEx with a &lt;code&gt;1..20&lt;/code&gt; range of phone numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  Task.async_stream&lt;span class="o"&gt;(&lt;/span&gt;1..20, fn phone_number -&amp;gt; send_message&lt;span class="o"&gt;(&lt;/span&gt;phone_number&lt;span class="o"&gt;)&lt;/span&gt; end&lt;span class="o"&gt;)&lt;/span&gt; |&amp;gt; Stream.run&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I noticed that the number of items processed at the same time was in a batch of 10 until completion. This number can differ depending on the machine used, so don't be surprised if you're seeing a batch of 20 or even 5 items processed simultaneously in your machine. This is because of the logical cores available in different machines. To confirm the number of logical cores available, use &lt;a href="https://hexdocs.pm/elixir/1.13/System.html#schedulers_online/0" rel="noopener noreferrer"&gt;&lt;code&gt;System.schedulers_online/0&lt;/code&gt;&lt;/a&gt;. For machines whose CPU has less than 4 logical cores, &lt;code&gt;Task.async_stream/3&lt;/code&gt; will appear to be slower.&lt;/p&gt;

&lt;p&gt;We can emulate the slow performance by setting &lt;code&gt;:max_concurrency&lt;/code&gt; to a value less than 4.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  Task.async_stream&lt;span class="o"&gt;(&lt;/span&gt;1..20, fn phone_number -&amp;gt; send_message&lt;span class="o"&gt;(&lt;/span&gt;phone_number&lt;span class="o"&gt;)&lt;/span&gt; end, max_concurrency: 2&lt;span class="o"&gt;)&lt;/span&gt; |&amp;gt; Stream.run&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the concurrency limit is set to 2, it takes more time to complete compared to using the default value set, 10. This is due to the fact that with a lower concurrency limit, fewer tasks are processed concurrently.&lt;/p&gt;

&lt;p&gt;When setting &lt;code&gt;max_concurrency&lt;/code&gt;, consider your system resources. Verify that the system has enough resources to handle the specified concurrency limit effectively and also bear in mind the need for performance. Depending on what works for your use case, you can decrease or increase the concurrency limit.&lt;/p&gt;

&lt;p&gt;Our application aims to send messages to a million phone numbers. At present, the &lt;code&gt;max_concurrency&lt;/code&gt; setting on my machine is set to 10 and is working satisfactorily. However, it is taking a long time to process all the messages. After 17 minutes, only about 3,000 messages have been sent. To improve the processing speed, we need to increase the concurrency limit. I shall set it to 100, allowing more items to be processed concurrently, while also ensuring we do not overload the system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 1..20
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; |&amp;gt; Task.async_stream&lt;span class="o"&gt;(&lt;/span&gt; fn phone_number -&amp;gt; send_message&lt;span class="o"&gt;(&lt;/span&gt;phone_number&lt;span class="o"&gt;)&lt;/span&gt; end, max_concurrency: 100&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; |&amp;gt; Stream.run&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After increasing the &lt;code&gt;max_concurrency&lt;/code&gt; to 100:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In 4 minutes, about 6,000 messages were sent.&lt;/li&gt;
&lt;li&gt;There is no spike in memory allocation when we open the &lt;code&gt;observer&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the observer memory allocation when &lt;code&gt;max_concurrency&lt;/code&gt; is set to 10:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aGaVEh83--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/concurrency-limit-10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aGaVEh83--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/concurrency-limit-10.png" alt="Concurrency limit of 10" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here it is when &lt;code&gt;max_concurrency&lt;/code&gt; is set to 100:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RwbTLAJY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/concurrency-limit-100.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RwbTLAJY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/concurrency-limit-100.png" alt="concurrency limit of 100" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;:ordered&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When we execute this code in &lt;code&gt;IEx&lt;/code&gt;, you will notice that the results are returned in the same order as the input data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 1..5
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; |&amp;gt; Task.async_stream&lt;span class="o"&gt;(&lt;/span&gt; fn phone_number -&amp;gt; send_message&lt;span class="o"&gt;(&lt;/span&gt;phone_number&lt;span class="o"&gt;)&lt;/span&gt; end, max_concurrency: 100&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; |&amp;gt; Enum.to_list

sending message  to 1
sending message  to 2
sending message  to 3
sending message  to 4
sending message  to 5

&lt;span class="o"&gt;[&lt;/span&gt;
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because we have the &lt;code&gt;:ordered&lt;/code&gt; option set to &lt;code&gt;true&lt;/code&gt; by default in the &lt;code&gt;Task.async_stream/3&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;However, setting &lt;code&gt;:ordered&lt;/code&gt; to true can result in slower processing if a task takes longer to complete. This is because &lt;code&gt;Task.async_stream/3&lt;/code&gt; will wait for a slow task to finish before moving on to the next one. Therefore, it is important to consider the trade-off between ordered results and processing speed when using this function.&lt;/p&gt;

&lt;p&gt;To improve processing speed, you can disable ordering by setting the &lt;code&gt;:ordered&lt;/code&gt; option to &lt;code&gt;false&lt;/code&gt;. This way, &lt;code&gt;Task.async_stream/3&lt;/code&gt; won't wait for a slow task to complete processing before moving on to the next task.&lt;/p&gt;

&lt;p&gt;Let's introduce a slow process in our &lt;code&gt;Sender&lt;/code&gt; application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Sender&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate slow processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to 3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Asynchronous version using Task.async_stream/3&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;send_message/1&lt;/code&gt; function accepts one argument, a phone number. If the phone number is 3, the function simulates a slow process by sleeping for 4 seconds. Otherwise, it simulates a regular process by sleeping for 3 seconds. The output result includes the phone number that's being processed, which allows us to check the order of the returned result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Sender.send_to_all&lt;span class="o"&gt;(&lt;/span&gt;1..5&lt;span class="o"&gt;)&lt;/span&gt;

sending message  to 1
sending message  to 2
sending message  to 4
sending message  to 5
sending message  to 3

&lt;span class="o"&gt;[&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 1&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 2&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 3&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 4&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 5&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The print message of number 3 is displayed last, indicating that it took longer to process before sending. However, the collected list is returned in the same order as the given input. This means that, while number 3 took longer to complete, numbers 4 and 5 had to wait before being operated on.&lt;/p&gt;

&lt;p&gt;In our case, we want to check whether the status of the messages sent to the given phone numbers was successful or not. To speed up things, let's disable the &lt;code&gt;:ordered&lt;/code&gt; option by setting it to &lt;code&gt;false&lt;/code&gt;, since we don't care about the order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Sender&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate slow processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to 3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Asynchronous version using Task.async_stream/3&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ordered:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Sender.send_to_all&lt;span class="o"&gt;(&lt;/span&gt;1..5&lt;span class="o"&gt;)&lt;/span&gt;

sending message  to 1
sending message  to 2
sending message  to 4
sending message  to 5
sending message  to 3

&lt;span class="o"&gt;[&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 1&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 2&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 4&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 5&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 3&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The number 3 is collected last in the returned result. This way, we can be sure that &lt;code&gt;Task.async_stream/3&lt;/code&gt; won't be idle waiting for the processing of number 3 to complete, before moving to the next.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;:timeout&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Task.async_stream/3&lt;/code&gt; function has a &lt;code&gt;:timeout&lt;/code&gt; option which sets a limit on how long each task can run. By default, the timeout is set to 5000 milliseconds or 5 seconds. You can specify the time limit in milliseconds or set it to &lt;code&gt;:infinity&lt;/code&gt; if you want to allow the task to run indefinitely. If a task takes longer than the specified timeout, it will raise an exception and terminate the current process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Sender&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate slow processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to 3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Asynchronous version using Task.async_stream/3&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ordered:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the processing time of the slow process/task is increased to 7000ms, which exceeds the default timeout in &lt;code&gt;Task.async_stream/3&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Sender.send_to_all&lt;span class="o"&gt;(&lt;/span&gt;1..5&lt;span class="o"&gt;)&lt;/span&gt;

sending message  to 1
sending message  to 2
sending message  to 4
sending message  to 5

&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; exited &lt;span class="k"&gt;in&lt;/span&gt;: Task.Supervised.stream&lt;span class="o"&gt;(&lt;/span&gt;5000&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;EXIT&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;time &lt;/span&gt;out
    &lt;span class="o"&gt;(&lt;/span&gt;elixir 1.15.5&lt;span class="o"&gt;)&lt;/span&gt; lib/task/supervised.ex:314: Task.Supervised.stream_reduce/7
    &lt;span class="o"&gt;(&lt;/span&gt;elixir 1.15.5&lt;span class="o"&gt;)&lt;/span&gt; lib/enum.ex:4387: Enum.reverse/1
    &lt;span class="o"&gt;(&lt;/span&gt;elixir 1.15.5&lt;span class="o"&gt;)&lt;/span&gt; lib/enum.ex:3704: Enum.to_list/1
    iex:3: &lt;span class="o"&gt;(&lt;/span&gt;file&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we execute the &lt;code&gt;Sender.send_to_all(1..5)&lt;/code&gt; function in IEx, an exception is raised, which stops the stream and crashes the current process. This is because the slow task takes more than 5000ms to complete, while &lt;code&gt;Task.async_stream/3&lt;/code&gt; has a timeout limit of 5000ms.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;:on_timeout&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It's difficult to predict the duration of a task, as several factors can contribute to its slow processing. For instance, processing a large amount of data can be time-consuming, as can waiting for a slow third-party API to respond.&lt;/p&gt;

&lt;p&gt;Instead of allowing &lt;code&gt;Task.async_stream/3&lt;/code&gt; to raise an exception that stops the stream, we can use the &lt;code&gt;:on_timeout&lt;/code&gt; option by setting it to &lt;code&gt;:kill_task&lt;/code&gt;. This option determines the action to take when the task times out. Setting it to &lt;code&gt;:kill_task&lt;/code&gt; will cause &lt;code&gt;Task.async_stream/3&lt;/code&gt; to ignore the process that exits with a timeout and continue with other tasks.&lt;/p&gt;

&lt;p&gt;By default, it's set to &lt;code&gt;:exit&lt;/code&gt;. This results in the task exceeding the timeout to stop the stream and crash the current process, as seen with the &lt;code&gt;:timeout&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Sender&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate slow processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Asynchronous version using Task.async_stream/3&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;ordered:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;on_timeout:&lt;/span&gt; &lt;span class="ss"&gt;:kill_task&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;code&gt;:on_timeout&lt;/code&gt; option and set it to &lt;code&gt;:kill_task&lt;/code&gt; in &lt;code&gt;Task.async_stream/3&lt;/code&gt;. Then, execute &lt;code&gt;Sender.send_to_all(1..5)&lt;/code&gt; in IEx. We can observe that the other tasks are processed completely, while the slow process is ignored, and returns &lt;code&gt;{:exit, :timeout}&lt;/code&gt; instead of crashing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Sender.send_to_all&lt;span class="o"&gt;(&lt;/span&gt;1..5&lt;span class="o"&gt;)&lt;/span&gt;

sending message  to 1
sending message  to 2
sending message  to 4
sending message  to 5

&lt;span class="o"&gt;[&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 1&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 2&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 4&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="o"&gt;{&lt;/span&gt;:ok, &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, 5&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="nb"&gt;exit&lt;/span&gt;: :timeout
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we wrap up, let's take a very quick look at monitoring memory using Observer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitor Memory with Erlang Observer
&lt;/h2&gt;

&lt;p&gt;Let's use Erlang Observer to monitor memory allocation. We can compare memory usage when defining the &lt;code&gt;send_to_all/1&lt;/code&gt; function using &lt;code&gt;Task.async_stream/3&lt;/code&gt; and using a combination of &lt;code&gt;Enum.map/2&lt;/code&gt; and &lt;code&gt;Task.async/1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Sender&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate slow processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Asynchronous version using Task.async_stream/3&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;max_concurrency:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;ordered:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;on_timeout:&lt;/span&gt; &lt;span class="ss"&gt;:kill_task&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Sender&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate slow processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulate some processing time&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sending message to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sent"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Asynchronous version using Task.async/1&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;send_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;phone_numbers&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="no"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uP8gaYSa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/efficient.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uP8gaYSa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/efficient.png" alt="efficient" width="800" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iTWRMeEx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/inefficient.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iTWRMeEx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2024-06/inefficient.png" alt="inefficient" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The combination of &lt;code&gt;Task.async/1&lt;/code&gt; and &lt;code&gt;Enum.map/2&lt;/code&gt; can significantly impact memory consumption compared to using &lt;code&gt;Task.async_stream/3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Task.async_stream/3&lt;/code&gt; is a powerful tool for effectively utilizing concurrency without overloading system resources when working with a series of tasks.&lt;/p&gt;

&lt;p&gt;Here are a couple of benefits of using &lt;code&gt;Task.async_stream/3&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It prevents sudden spikes in system usage: By setting the concurrency limit option, you can control the number of concurrent tasks being executed at any given time. This helps prevent the system from becoming overwhelmed with too many tasks running simultaneously, which could lead to spikes in resource usage, such as memory or CPU.&lt;/li&gt;
&lt;li&gt;It handles back pressure: Back pressure occurs when there's resistance to the progress of turning input to output. By setting the concurrency limit, &lt;code&gt;Task.async_stream/3&lt;/code&gt; ensures that tasks are processed at a manageable rate, preventing resource exhaustion.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Elixir has several tools that are useful when it comes to leveraging concurrency, and Task processes is one of them.&lt;/p&gt;

&lt;p&gt;In this post, we explored the concept of concurrency and some of the issues we might face when dealing with a series of tasks while using concurrency.&lt;/p&gt;

&lt;p&gt;We then explored the use of &lt;code&gt;Task.async_stream/3&lt;/code&gt;, a Task module function that is effective in handling a large number of tasks, as it provides both concurrency and performance.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
    <item>
      <title>How to Build a Memory-efficient Elixir App with Streams</title>
      <dc:creator>Tracey Onim</dc:creator>
      <pubDate>Tue, 20 Feb 2024 15:51:27 +0000</pubDate>
      <link>https://dev.to/appsignal/how-to-build-a-memory-efficient-elixir-app-with-streams-pc9</link>
      <guid>https://dev.to/appsignal/how-to-build-a-memory-efficient-elixir-app-with-streams-pc9</guid>
      <description>&lt;p&gt;We have all encountered collections of data at some point when working on Elixir applications. These collections are very handy for storing, retrieving, and manipulating data using different data structures, making them very efficient in managing clean code.&lt;/p&gt;

&lt;p&gt;In this article, we'll go through the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are collections in Elixir?&lt;/li&gt;
&lt;li&gt;The problems likely to be faced when working with collections&lt;/li&gt;
&lt;li&gt;The greedy approach when working with large datasets&lt;/li&gt;
&lt;li&gt;The enumerable in Elixir, used to work around collections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we'll explore how to build a memory-efficient Elixir application using the lazy processing approach with streams.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Collections in Elixir?
&lt;/h2&gt;

&lt;p&gt;You can view a collection as a container that groups multiple things/objects/elements into a single unit. This collection allows you to store, retrieve, and manipulate data.&lt;/p&gt;

&lt;p&gt;A couple of examples of collections encountered in the real world include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A mail folder that contains received and sent letters.&lt;/li&gt;
&lt;li&gt;A phone directory mapping phone numbers to corresponding names.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Elixir, &lt;code&gt;List&lt;/code&gt;, &lt;code&gt;Maps&lt;/code&gt;, &lt;code&gt;Ranges&lt;/code&gt;, &lt;code&gt;files&lt;/code&gt;, and &lt;code&gt;tuples&lt;/code&gt; are the most common types of collections. Some of these collections can be iterated through. Each element is passed through once, in order, so we can term them &lt;code&gt;enumerables&lt;/code&gt;. Things that can be iterated in Elixir implement the &lt;a href="https://hexdocs.pm/elixir/Enumerable.html#functions" rel="noopener noreferrer"&gt;Enumerable protocol&lt;/a&gt;. &lt;code&gt;List&lt;/code&gt;, &lt;code&gt;Maps&lt;/code&gt;, and &lt;code&gt;Ranges&lt;/code&gt; are the most common datatypes used as enumerables.&lt;/p&gt;

&lt;p&gt;Elixir has two modules with iteration functions that use the Enumerable protocol: &lt;a href="https://hexdocs.pm/elixir/1.12/Enum.html#content" rel="noopener noreferrer"&gt;Enum&lt;/a&gt; and &lt;a href="https://hexdocs.pm/elixir/1.12/Stream.html" rel="noopener noreferrer"&gt;Stream&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenges of Collections
&lt;/h2&gt;

&lt;p&gt;Working with collections can be very efficient, especially when your collections aren't that large. However, manipulating these collections can be very difficult when dealing with large datasets.&lt;/p&gt;

&lt;p&gt;For example, let's say we want to retrieve organization data from a CSV file and display it in our application. Ideally, we have to load all the organizations from the CSV file into an application's memory and transform the retrieved data into the data we want.&lt;/p&gt;

&lt;p&gt;Our application can be safe if the memory contains a predictable number of organizations that can be easily transformed or manipulated. However, if we have extensive data on organizations retrieved from the CSV, managing this data can result in slow application performance, because more memory is needed to consume it.&lt;/p&gt;

&lt;p&gt;So, how can we elegantly manage our large CSV file? (keeping in mind some of the problems we might face while processing it).&lt;/p&gt;

&lt;p&gt;We can process our CSV file using one of two approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The greedy approach that consumes more memory.&lt;/li&gt;
&lt;li&gt;The memory-efficient lazy processing approach.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Earlier, we mentioned that Elixir has two modules, &lt;code&gt;Enum&lt;/code&gt; and &lt;code&gt;Stream&lt;/code&gt;, with iteration functions. We'll use these modules to process our CSV file.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Greedy Approach For Our Elixir Application
&lt;/h3&gt;

&lt;p&gt;In the greedy approach, we are going to use the Enum module because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When given a collection, it will consume all the content of that collection. That means the whole collection should be loaded into memory before being processed.&lt;/li&gt;
&lt;li&gt;The returned result is another collection that will be loaded into memory, which might be inefficient if data is not needed at that time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's process our CSV file greedily.&lt;/p&gt;

&lt;p&gt;We shall use an organization's CSV file with 2,000,000 records &lt;a href="https://www.datablist.com/learn/csv/download-sample-csv-files" rel="noopener noreferrer"&gt;downloaded from here&lt;/a&gt; for demonstration purposes. The size of the CSV file (after unzipping) is around 283.4MB.&lt;/p&gt;

&lt;p&gt;Our focus will be to get the records of all organizations and transform them into data we can easily display in the browser. The result should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="p"&gt;%{&lt;/span&gt;
&lt;span class="ss"&gt;index:&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="ss"&gt;organization_id:&lt;/span&gt; &lt;span class="s2"&gt;"1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"PLC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="ss"&gt;website:&lt;/span&gt; &lt;span class="s2"&gt;"https://park.com/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="ss"&gt;country:&lt;/span&gt; &lt;span class="s2"&gt;"Kenya"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Focused for the best"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="ss"&gt;founded:&lt;/span&gt; &lt;span class="s2"&gt;"2016"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="ss"&gt;industry:&lt;/span&gt; &lt;span class="s2"&gt;"sports"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="ss"&gt;num_of_employees:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;

&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we begin, let's open the interactive shell to manipulate the CSV file. After that, start the &lt;code&gt;Erlang Observer&lt;/code&gt; application by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; :observer.start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We shall use the &lt;code&gt;Observer&lt;/code&gt; application to inspect memory allocators when processing the CSV file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Loading the CSV File Into Memory
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;File.read!("organization.csv")&lt;/code&gt; to load the whole CSV file into memory. The observer shows an extra 283.84 MB allocated to memory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-step1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-step1.png" alt="file opening"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;org_csv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"organization.csv"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


    &lt;span class="s2"&gt;"Index,Organization Id,Name,Website,Country,Description,Founded,Industry,Number of employees&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;1,391dAA77fea9EC1,Daniel-Mcmahon,https://stuart-rios.biz/,Cambodia,Focused eco-centric help-desk,2013,Sports,1878&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;2,9FcCA4A23e6BcfA, Mcdowell,http://jacobs.biz/,Guyana,Front-line real-time portal,2018,Legal Services,9743&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;3,DB23330238B7B3D,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Roberts, Carson and Trujillo&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,http://www.park.com/,Jordan,Innovative hybrid data-warehouse,1992,Hospitality,7537&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;4,bbf18835CFbEee7,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Poole, Jefferson and Merritt&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,http://hayden.com/,Cocos (Keeling) Islands,Extended regional Graphic Interface,1991,Food Production,9974&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;5,74ECD725ceaDfd9,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Ritter, Patel and Cisneros&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,https://www.mason-blackwell.info/,Ecuador,Re-contextualized actuating website,2019,Computer Networking,5050&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;6,ea42e2FECDAdF0c,Stafford Ltd,http://www.fuller.biz/,Qatar,Multi-channeled optimizing customer loyalty,1979,Aviation / Aerospace,7506&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;7,b8EE5AE2Df8BA46,Roach Ltd,https://www.oconnell.com/,Guatemala,Switchable explicit complexity,2020,Hospitality,109&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;8,21829Cf5a968024,Gill PLC,https://www.berry.com/,Lesotho,Future-proofed systemic pricing structure,1983,Printing,1822&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;9,f9C605c034f47cD,Summers-Jordan,https://le.net/,Luxembourg,Cross-platform bottom-line system engine,1976,Primary / Secondary Education,1603&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;10,0fBa55ecDA6cb4B,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Richard, Lane and Weaver&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,https://bauer-hardy.com/,United States Minor Outlying Islands,Optimized system-worthy complexity,2006,Building Materials,3505&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;11,d085befF19Be6fB,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Harrington, Sutton and Wilkins&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,http://henson.com/,Guinea-Bissau,Diverse scalable instruction set,1985,Airlines / Aviation,1926&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;12,13B77fAF602E949,Evans LLC,http://harrington-powers.com/,Cook Islands,Re-contextualized stable flexibility,2017,Design,3338&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;13,90234C8d0D7eB75,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Patterson, Deleon and Donovan&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,https://thornton.net/,Holy See (Vatican City State),Versatile encompassing migration,1970,Automotive,9312&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;14,155A7db5D8Cce47,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Carlson, Snyder and Holland&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,http://foley.com/,Korea,Devolved optimal secured line,1979,International Trade / Development,2649&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;15,77B7F7fb1Ac2A44,Duffy-Stark,https://www.morales.com/,Monaco,Re-contextualized 24/7 Graphical User Interface,1990,Aviation / Aerospace,1714&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;16,1f6bABBA98cd33E,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Frederick, Fry and Poole&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,http://www.madden.org/,Vanuatu,Synchronized empowering structure,2008,Fishery,1862&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;17,05dbf87Ee09b6BC,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Walton, Proctor and Peters&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,http://www.casey-bell.com/,Grenada,Diverse local collaboration,1979,Alternative Medicine,4678&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;18,20ef50A2fdB3993,Oneal and Sons,http://munoz.org/,Fiji,Operative uniform definition,1987,Religious Institutions,8332&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;19,a9C703D1A2B074C,Maynard-Bush,http://leblanc.com/,Senegal,Customizable zero tolerance functionalities,2011,Apparel / Fashion,1668&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;20,1ea33Ac2face255,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Rosario, Martin and White&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,https://www.mueller-rhodes.net/,Isle of Man,Synchronized 6thgeneration flexibility,1994,Music,5134&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;21,CDCAe4a6DA0eC6d,Brady Ltd,http://montgomery.biz/,Guyana,Self-enabling modular archive,1989,Professional Training,5153&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;22,f41b9Ce917bDD28,Odom Group,http://www.riley-dalton.net/,Saint Kitts and Nevis,Grass-roots bandwidth-monitored projection,2003,Computer Hardware,4793&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;23,B1181c8C3d43216,Stanton-Arroyo,http://baird.org/,Montserrat,Integrated empowering core,1976,Mental Health Care,6919&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;24,b3E9C4E4cbC4a8e,Stafford-Hickman,https://love.net/,United States Virgin Islands,Virtual background application,1996,Hospital / Health Care,3855&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;25,f92AacaEEcB6eC6,Ryan and Sons,http://koch-raymond.org/,Chad,Public-key maximized definition,2017,Airlines / Aviation,1564&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;26,8eb91E6eeDBC2A5,Wolf Group,https://aguilar-solomon.net/,Cayman Islands,Public-key value-added alliance,1984,Package / Freight Delivery,4509&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;27,fDaeD26dB6fd79a,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Horne, Bailey and Oconnor&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,https://cruz.com/,Western Sahara,Customer-focused needs-based service-desk,1973,Wholesale,2670&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;28,19Bbb8dB90dFaFF,Cobb Inc,http://miles.com/,Netherlands,Assimilated local ability,2008,Political Organization,2494&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;29,00AF7EacF3fEb91,Parrish-Peterson,https://strong.org/,France,Multi-lateral encompassing structure,2004,Political Organization,9059&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;30,8Ec27fFD6b945A9,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Barr"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a List for Each Row In the CSV
&lt;/h3&gt;

&lt;p&gt;The data in Step 1 is loaded text, which means we are still far from our end goal. In this step, let's take the loaded text data and split it into lines. This will result in a list of string elements, with each element representing a row of the CSV.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_header&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;org_csv&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Index,Organization Id,Name,Website,Country,Description,Founded,Industry,Number of employees&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"1,391dAA77fea9EC1,Daniel-Mcmahon,https://stuart-rios.biz/,Cambodia,Focused eco-centric help-desk,2013,Sports,1878&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;"2,9FcCA4A23e6BcfA,Mcdowell, http://jacobs.biz/,Guyana,Front-line real-time portal,2018,Legal Services,9743&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The observer shows an increase in allocated memory:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-step2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-step2.png" alt="step 2 greedy approach"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Create a Column from Each Row
&lt;/h3&gt;

&lt;p&gt;In the second step, we pattern-matched our result to &lt;code&gt;[_header | rows]&lt;/code&gt;. We are not interested in the header of the CSV file, but in the body.&lt;/p&gt;

&lt;p&gt;In this step, we will split each row into a list, with the elements in each list representing a column of the CSV file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;org_records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"391dAA77fea9EC1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Daniel-Mcmahon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"https://stuart-rios.biz/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="s2"&gt;"Cambodia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Focused eco-centric help-desk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"2013"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Sports"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1878&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"9FcCA4A23e6BcfA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Mcdowell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="s2"&gt;"http://jacobs.biz/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Guyana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Front-line real-time portal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"2018"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="s2"&gt;"Legal Services"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"9743&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step alone takes almost 60 seconds to process. The observer also shows a spike increase in memory allocation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-step3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-step3.png" alt="step 3 greedy approach"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Create a Keyword List of Each Row
&lt;/h3&gt;

&lt;p&gt;Here, each column in each row will be zipped to its corresponding header name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;org_records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;org_records&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:org_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:website&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:founded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:industry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:number_of_employees&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
       &lt;span class="ss"&gt;index:&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;org_id:&lt;/span&gt; &lt;span class="s2"&gt;"391dAA77fea9EC1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"Daniel-Mcmahon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;website:&lt;/span&gt; &lt;span class="s2"&gt;"https://stuart-rios.biz/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;country:&lt;/span&gt; &lt;span class="s2"&gt;"Cambodia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Focused eco-centric help-desk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;founded:&lt;/span&gt; &lt;span class="s2"&gt;"2013"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;industry:&lt;/span&gt; &lt;span class="s2"&gt;"Sports"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;number_of_employees:&lt;/span&gt; &lt;span class="s2"&gt;"1878"&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="ss"&gt;index:&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;org_id:&lt;/span&gt; &lt;span class="s2"&gt;"9FcCA4A23e6BcfA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"Mcdowell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;website:&lt;/span&gt; &lt;span class="s2"&gt;"http://jacobs.biz/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;country:&lt;/span&gt; &lt;span class="s2"&gt;"Guyana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Front-line real-time portal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;founded:&lt;/span&gt; &lt;span class="s2"&gt;"2018"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;industry:&lt;/span&gt; &lt;span class="s2"&gt;"Legal Services"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;number_of_employees:&lt;/span&gt; &lt;span class="s2"&gt;"9743"&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we use &lt;a href="https://hexdocs.pm/elixir/1.12/Enum.html#map/2" rel="noopener noreferrer"&gt;Enum.map/2&lt;/a&gt; and &lt;a href="https://hexdocs.pm/elixir/1.12/Enum.html#zip/2" rel="noopener noreferrer"&gt;Enum.zip/2&lt;/a&gt; to process the organization record results from step 3 (a list of lists, where each list element represents a row of records). Remember that each call to the Enum module takes a collection and returns a collection. Both &lt;code&gt;Enum.map/2&lt;/code&gt; and &lt;code&gt;Enum.zip/2&lt;/code&gt; will greedily process our data, which explains the spike increase in memory allocation as seen from the observer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-final-step.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-final-step.png" alt="step 4 greedy approach"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Enum.map/2&lt;/code&gt; takes in a list of lists, where each list element is a row of organization records. It iterates through each corresponding row and invokes &lt;code&gt;Enum.zip/2&lt;/code&gt; on each row to transform it into a keyword list.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Enum.zip/2&lt;/code&gt; function takes in two enumerables. The first enumerable is a list of header names, and the second is a list of each row. The &lt;code&gt;zip&lt;/code&gt; function zips the corresponding elements from the list of headers and the list of each row into a list of tuples. The tuple element in the list has an atom as the first element (which is the header name and value of the corresponding header name).&lt;/p&gt;

&lt;p&gt;Elixir represents this kind of output as a keyword list. In your interactive shell, you will notice each row is transformed into a keyword list, but not a list of tuples.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Enum.map/2&lt;/code&gt; returns a new list of keyword lists.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Convert to Map
&lt;/h3&gt;

&lt;p&gt;Remember, our end goal was to work with data that's easy to manipulate when we want to display a list of organizations. In this final step, we will convert the keyword list above into maps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;org_records&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;%{&lt;/span&gt;
        &lt;span class="ss"&gt;index:&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;org_id:&lt;/span&gt; &lt;span class="s2"&gt;"391dAA77fea9EC1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"Daniel-Mcmahon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;website:&lt;/span&gt; &lt;span class="s2"&gt;"https://stuart-rios.biz/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;country:&lt;/span&gt; &lt;span class="s2"&gt;"Cambodia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Focused eco-centric help-desk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;founded:&lt;/span&gt; &lt;span class="s2"&gt;"2013"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;industry:&lt;/span&gt; &lt;span class="s2"&gt;"Sports"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;number_of_employees:&lt;/span&gt; &lt;span class="s2"&gt;"1878"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;%{&lt;/span&gt;
        &lt;span class="ss"&gt;index:&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;org_id:&lt;/span&gt; &lt;span class="s2"&gt;"9FcCA4A23e6BcfA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s2"&gt;"Mcdowell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;website:&lt;/span&gt; &lt;span class="s2"&gt;"http://jacobs.biz/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;country:&lt;/span&gt; &lt;span class="s2"&gt;"Guyana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Front-line real-time portal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;founded:&lt;/span&gt; &lt;span class="s2"&gt;"2018"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;industry:&lt;/span&gt; &lt;span class="s2"&gt;"Legal Services"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;number_of_employees:&lt;/span&gt; &lt;span class="s2"&gt;"9743"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiling everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="s2"&gt;"organization.csv"&lt;/span&gt;
 &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="n"&gt;rows&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:org_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:website&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:founded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:industry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:number_of_employees&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Observation with &lt;code&gt;Enum&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In each &lt;code&gt;Enum&lt;/code&gt; step, we generated an output before the final result. The intermediate result in each step is stored in memory as a full collection. That's because, with Enum implementation, the whole collection must be available for the next step in the pipeline to start processing.&lt;/li&gt;
&lt;li&gt;With Enum, each step has to wait for its intermediate result to start processing, so it takes more time to process data in each step.&lt;/li&gt;
&lt;li&gt;There is a spike increase in memory allocation in each step, meaning an intermediate output must be kept in memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The greedy approach, or working with Enum, works well with a predictable number of elements. We couldn't have easily noticed these problems in a small CSV file.&lt;/p&gt;

&lt;p&gt;However, this approach is very inefficient when we have to keep an intermediate result in memory in each step. This is a massive waste of memory that can lead to slow application performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: Build a Memory-efficient Elixir App with Streams
&lt;/h2&gt;

&lt;p&gt;First, what are our goals?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memory allocation that's only for the final result.&lt;/li&gt;
&lt;li&gt;To process records only when we need them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using streams, we can do these things. Streams are known to be lazy enumerables.&lt;/p&gt;

&lt;p&gt;This is because streams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lazily load data into memory: That is, one element at a time instead of loading everything at once.&lt;/li&gt;
&lt;li&gt;Only load data when needed.&lt;/li&gt;
&lt;li&gt;Instead of applying a transformation to the given enumerable, return a value with the intended specifications.&lt;/li&gt;
&lt;li&gt;Process one piece of data at a time as it arrives instead of waiting for the whole collection to be available.&lt;/li&gt;
&lt;li&gt;Are a composable enumerator that reads and pipes one single line at a time without needing to wait for all the passed data to be processed at every single step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Lazy Processing Approach with Streams in Elixir
&lt;/h3&gt;

&lt;p&gt;We can leverage lazy processing to process our organization's CSV file.&lt;/p&gt;

&lt;p&gt;The good thing is that more Elixir modules now support streams. Instead of opening the CSV file and loading the data on step 1, let's use &lt;a href="https://hexdocs.pm/elixir/1.13/File.html#stream!/3" rel="noopener noreferrer"&gt;&lt;code&gt;File.stream!/3&lt;/code&gt;&lt;/a&gt; to create a stream without necessarily opening the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; File.stream!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/org.csv"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    %File.Stream&lt;span class="o"&gt;{&lt;/span&gt;
      line_or_bytes: :line,
      modes: &lt;span class="o"&gt;[&lt;/span&gt;:raw, :read_ahead, :binary],
      path: &lt;span class="s2"&gt;"/org.csv"&lt;/span&gt;,
      raw: &lt;span class="nb"&gt;true&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;File.stream!/3&lt;/code&gt; function returns &lt;code&gt;%File.Stream{}&lt;/code&gt; with intended specifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Ffile-stream.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Ffile-stream.png" alt="File.stream"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The observer doesn't reflect our CSV file's extra memory size, meaning the file still needs to be opened and loaded into memory.&lt;/p&gt;

&lt;p&gt;Streams are composable enumerables. We can pipe one stream to another. We will replace the Enum functions from steps 2-4 of the greedy approach with stream functions. We'll avoid processing data and loading it into memory until we decide to run the stream (compute/process the data as intended) by passing it to a function in the Enum module.&lt;/p&gt;

&lt;p&gt;Let's compose our first part of the pipeline with streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; header &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;:index, :org_id, :name, :website, :country, :description, :founded, :industry, :number_of_employees]
iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;"/org.csv"&lt;/span&gt;
|&amp;gt; File.Stream!&lt;span class="o"&gt;()&lt;/span&gt;
|&amp;gt; Stream.drop&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
|&amp;gt; Stream.map&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;String.split&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;1, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, &lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, trim: &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
|&amp;gt; Stream.map&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;Stream.zip&lt;span class="o"&gt;(&lt;/span&gt;header, &amp;amp;1&lt;span class="o"&gt;))&lt;/span&gt;
|&amp;gt; Stream.map&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;&lt;span class="o"&gt;(&lt;/span&gt;Map.new&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;1&lt;span class="o"&gt;)))&lt;/span&gt;

  &lt;span class="c"&gt;#Stream&amp;lt;[&lt;/span&gt;
  enum: %File.Stream&lt;span class="o"&gt;{&lt;/span&gt;
    path:  &lt;span class="s2"&gt;"/org.csv"&lt;/span&gt;,
    modes: &lt;span class="o"&gt;[&lt;/span&gt;:raw, :read_ahead, :binary],
    line_or_bytes: :line,
    raw: &lt;span class="nb"&gt;true&lt;/span&gt;,
    node: :nonode@nohost
  &lt;span class="o"&gt;}&lt;/span&gt;,
  funs: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="c"&gt;#Function&amp;lt;34.53678557/1 in Stream.drop/2&amp;gt;,&lt;/span&gt;
   &lt;span class="c"&gt;#Function&amp;lt;48.53678557/1 in Stream.map/2&amp;gt;,&lt;/span&gt;
   &lt;span class="c"&gt;#Function&amp;lt;48.53678557/1 in Stream.map/2&amp;gt;,&lt;/span&gt;
   &lt;span class="c"&gt;#Function&amp;lt;48.53678557/1 in Stream.map/2&amp;gt;]&lt;/span&gt;
&lt;span class="o"&gt;]&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The composed pipeline of streams above returns a stream that only stores the intended computation instead of applying the transformation.&lt;/p&gt;

&lt;p&gt;Here is what is happening within our stream processing pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of opening the file, we use &lt;code&gt;File.Stream/3&lt;/code&gt; to create a stream.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/elixir/1.15/Stream.html#drop/2" rel="noopener noreferrer"&gt;Stream.drop/2&lt;/a&gt; lazily drops the first element, the header.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/elixir/1.15/Stream.html#map/2" rel="noopener noreferrer"&gt;Stream.map/2&lt;/a&gt;:

&lt;ul&gt;
&lt;li&gt;splits data into columns&lt;/li&gt;
&lt;li&gt;lazily zips each row into its corresponding header name with &lt;a href="https://hexdocs.pm/elixir/1.15/Stream.html#zip/2" rel="noopener noreferrer"&gt;Stream.zip/2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;transforms each piece of data into a map&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To process our desired result, we have to pass the stream value to a function in the Enum module.&lt;br&gt;
First, let's take only the first 3 elements from our stream and observe what happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;trim:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;label:&lt;/span&gt; &lt;span class="s2"&gt;"============="&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://hexdocs.pm/elixir/Enum.html#take/2" rel="noopener noreferrer"&gt;Enum.take/2&lt;/a&gt; receives the transformed map data from the stream and only gets the first 3 elements. Once it gets the 3 elements from the stream, there is no more processing.&lt;/p&gt;

&lt;p&gt;Running this pipeline, you will notice that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The result is returned instantaneously.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IO.inspect&lt;/code&gt; only prints the first 3 elements. This is proof that the elements are being processed one at a time by subsequent calls to Enum functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lazy Approach Using Streams Vs. Greedy Approach
&lt;/h2&gt;

&lt;p&gt;Let's use the greedy approach to take the first 3 elements from our code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;csv&lt;/span&gt;
 &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="n"&gt;rows&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:org_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:website&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:founded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:industry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:number_of_employees&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;label:&lt;/span&gt; &lt;span class="s2"&gt;"+++++++++++++++++"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;IO.inspect&lt;/code&gt; prints the whole 2,000,000 records. This shows that with Enum, we have to wait for the whole collection to be available upfront before starting the next processing batch. Using this approach, an intermediate list of 2,000,000 records has to be kept in memory at each step (even if, in the end, we only get the first 3 records using &lt;code&gt;Enum.take/2&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Unlike the greedy approach, using the lazy approach, there is no need to process the whole collection for the next process to take place. By passing &lt;code&gt;Enum.take/2&lt;/code&gt; to fetch the first 3 elements, we can see there isn't even a peak in memory.&lt;/p&gt;

&lt;p&gt;With streams, even if we process 2,000,000 records, we can decide the amount of records to load in memory and ensure that the amount of utilized memory stores the final result. This is not the same case with Enum, since in each step there is a certain amount of records kept in memory.&lt;/p&gt;

&lt;p&gt;We can also compare the greedy and lazy approaches by monitoring memory allocators using the Erlang observer.&lt;/p&gt;

&lt;p&gt;Remember, in the greedy approach, we processed all records. We will do the same with our lazy approach by passing the stream pipeline to &lt;a href="https://hexdocs.pm/elixir/Enum.html#to_list/1" rel="noopener noreferrer"&gt;Enum.to_list/1&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;trim:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;label:&lt;/span&gt; &lt;span class="s2"&gt;"============="&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the memory allocated to the lazy approach:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Flazy-approach.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Flazy-approach.png" alt="Lazy Approach"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to the greedy approach:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-final-step.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2024-02%2Fgreedy-approach-final-step.png" alt="step 4 greedy approach"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;In this post, we first took a look at collections in Elixir and some of their associated challenges. We then explored two methods of processing large datasets: the greedy approach and the lazy approach using streams.&lt;/p&gt;

&lt;p&gt;We've seen how working with streams is one of the best approaches to help create a memory-efficient Elixir application.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
    <item>
      <title>Debugging Phoenix LiveView with open_browser/2</title>
      <dc:creator>Tracey Onim</dc:creator>
      <pubDate>Tue, 04 Apr 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/appsignal/debugging-phoenix-liveview-with-openbrowser2-b7d</link>
      <guid>https://dev.to/appsignal/debugging-phoenix-liveview-with-openbrowser2-b7d</guid>
      <description>&lt;p&gt;In this blog post, you'll see how useful &lt;code&gt;open_browser/2&lt;/code&gt; is when debugging LiveView tests. In addition, we'll give a brief introduction to testing LiveView.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing LiveView Testing
&lt;/h2&gt;

&lt;p&gt;Testing during the software development process is one way to build confidence and ensure your application will work as expected.&lt;/p&gt;

&lt;p&gt;The ability to easily write meaningful tests is an important factor for any framework, regardless of programming language.&lt;/p&gt;

&lt;p&gt;Developers can easily test the LiveView framework's component, life-cycle, and behavior by writing LiveView tests with pure Elixir, since it uses ExUnit (a built-in testing framework) for all its testing. We can be confident in writing LiveView tests that are fast, concurrent, and stable.&lt;/p&gt;

&lt;p&gt;You can test the functionality of your live views' behavior through the help of the &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveViewTest.html#content"&gt;&lt;code&gt;Phoenix.LiveViewTest&lt;/code&gt;&lt;/a&gt; module, which offers convenient functions without the need to introduce JS testing frameworks. The test helpers assist us in writing meaningful tests for our LiveView modules with ease and speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample Feature in LiveView
&lt;/h2&gt;

&lt;p&gt;Let's write a sample feature to use as our testing example.&lt;/p&gt;

&lt;p&gt;We shall add a form to our application that allows users to enter their email address and password when attempting to register. This feature will only focus on the rendered HTML and not include the ability to add users into the system (the backend part of taking user params and adding them into the data store).&lt;/p&gt;

&lt;p&gt;Let's begin!&lt;/p&gt;

&lt;p&gt;First, start by creating a new LiveView application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix phx.new sample_live &lt;span class="nt"&gt;--live&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NB: You can use an existing LiveView application if you have one.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;mix phx.new&lt;/code&gt; command with the &lt;code&gt;--live&lt;/code&gt; flag will create a new application with LiveView installed and configured.&lt;/p&gt;

&lt;p&gt;Add a live path in the router.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/sample_live_web/router.ex&lt;/span&gt;

&lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SampleLiveWeb&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;live&lt;/span&gt; &lt;span class="s2"&gt;"/user_registration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;RegistrationLive&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;registration_live.ex&lt;/code&gt; inside the &lt;code&gt;sample_live_web&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/sample_live_web/registration_live.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;SampleLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RegistrationLive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;SampleLiveWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:live_view&lt;/span&gt;


  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;~H""&lt;/span&gt;&lt;span class="s2"&gt;"

      &amp;lt;.form let={f} for={:changeset} id={"&lt;/span&gt;&lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="s2"&gt;"} &amp;gt;

        &amp;lt;%= label f, :email %&amp;gt;
        &amp;lt;%= text_input f, :email, id: "&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="s2"&gt;" %&amp;gt;
        &amp;lt;%= error_tag f, :email %&amp;gt;
        &amp;lt;%= label f, :password, id: "&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="s2"&gt;" %&amp;gt;
        &amp;lt;%= password_input f, :password %&amp;gt;
        &amp;lt;%= error_tag f, :password %&amp;gt;
        &amp;lt;%= submit "&lt;/span&gt;&lt;span class="no"&gt;Save&lt;/span&gt;&lt;span class="s2"&gt;" %&amp;gt;
      &amp;lt;/.form&amp;gt;

    """&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our LiveView application module, two callbacks have been defined: &lt;code&gt;mount/3&lt;/code&gt; and &lt;code&gt;render/1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;mount/3&lt;/code&gt; callback expects three arguments — &lt;code&gt;params&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt;, and &lt;code&gt;liveview socket&lt;/code&gt; — and returns &lt;code&gt;{:ok, socket}&lt;/code&gt;. When the LiveView page is rendered, the &lt;code&gt;mount/3&lt;/code&gt; callback will be invoked twice: once to perform the initial page load and again to establish the live connection.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;render/1&lt;/code&gt; callback is responsible for displaying the &lt;code&gt;HTML&lt;/code&gt; template/content — in this case, the added registration form. It receives the &lt;code&gt;socket assigns&lt;/code&gt; and must return a template passed inside the &lt;code&gt;~H sigil&lt;/code&gt;. Whenever there is a change in our LiveView, the &lt;code&gt;render/1&lt;/code&gt; callback will be invoked.&lt;/p&gt;

&lt;p&gt;In our template, we have defined a sample form where a user can enter their email and password.&lt;/p&gt;

&lt;p&gt;Let's start the server and open the registration form in our browsers. You should see something similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Oe_7_tAp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2023-03/registration-page.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Oe_7_tAp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2023-03/registration-page.png" alt="registration_page" width="880" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Display the Feature on the Browser while Testing
&lt;/h2&gt;

&lt;p&gt;When working on an HTTP-based web application, we usually write integration tests to validate passing expected attributes and properties to parts of our application.&lt;/p&gt;

&lt;p&gt;We'll write an integration test validating user interactions with our application. The test should verify that when a user visits the &lt;code&gt;/user_registration&lt;/code&gt; page, they can see the registration form properties/attributes rendered. In addition, we'll explore how we can use &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveViewTest.html#open_browser/2"&gt;open_browser/2&lt;/a&gt; to debug our LiveView test.&lt;/p&gt;

&lt;p&gt;First, let's write a test for our form implemented above that will verify the HTML rendered. In here, we shall use &lt;code&gt;open_browser/2&lt;/code&gt; to verify the displayed form. The implemented registration form has an HTML &lt;code&gt;id&lt;/code&gt; (&lt;code&gt;"registration-form"&lt;/code&gt;) that should help us select the element in our test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/sample_live_web/registration_live_test.exs&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;SampleLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RegistrationLiveTest&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;SamplLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ConnCase&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LiveViewTest&lt;/span&gt;

  &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"user can see registration form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;conn:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;live&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/user_registration"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;view&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#registration-form"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;open_browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="s2"&gt;"Email&amp;lt;/label&amp;gt;"&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="s2"&gt;"Password&amp;lt;/label&amp;gt;"&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;import Phoenix.LiveViewTest&lt;/code&gt; module gets access to the test helper functions.&lt;/p&gt;

&lt;p&gt;In the sample feature, when a user is on the application and wants to visit the registration page, they will navigate to &lt;code&gt;/user_registration&lt;/code&gt;. A similar thing happens here — we shall navigate the user to &lt;code&gt;/user_registration&lt;/code&gt; using &lt;code&gt;live/2&lt;/code&gt;, which performs a regular &lt;code&gt;get(conn, path)&lt;/code&gt; and then upgrades the page to LiveView. It takes in the &lt;code&gt;conn&lt;/code&gt; and the &lt;code&gt;path&lt;/code&gt; then returns a three-element tuple with &lt;code&gt;:ok&lt;/code&gt;, &lt;code&gt;liveview process&lt;/code&gt;, and the &lt;code&gt;rendered HTML&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;open_browser/2&lt;/code&gt; expects a &lt;code&gt;view&lt;/code&gt; or &lt;code&gt;element&lt;/code&gt; as the first argument. In the test, I have opted to pass an element, and this can be done by invoking &lt;code&gt;element/3&lt;/code&gt;, passing the &lt;code&gt;view&lt;/code&gt; and the &lt;code&gt;query selector&lt;/code&gt; to it. &lt;code&gt;element/3&lt;/code&gt; returns an &lt;code&gt;element&lt;/code&gt; which is then passed to &lt;code&gt;open_browser/2&lt;/code&gt;. If &lt;code&gt;open_browser/2&lt;/code&gt; finds the form element with the &lt;code&gt;query selector&lt;/code&gt;(#registration-form) within the LiveView page, we expect the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The default browser to be opened displaying the HTML of the form element.&lt;/li&gt;
&lt;li&gt;The form element to be returned.
The &lt;code&gt;form element&lt;/code&gt; returned can be passed to &lt;code&gt;render/1&lt;/code&gt;, which takes in a &lt;code&gt;view_or_element&lt;/code&gt; and returns an HTML string of the view that we can finally assert in the test.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix &lt;span class="nb"&gt;test test&lt;/span&gt;/sampl_live_web/registration_live_test.exs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should expect the default browser to open and our test to pass.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HwMsjWry--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2023-03/registration-page-open-browser.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HwMsjWry--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2023-03/registration-page-open-browser.png" alt="registration page opened by open_browser" width="880" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's make the user enter their email and password by using &lt;code&gt;form/3&lt;/code&gt;, which takes in the &lt;code&gt;view&lt;/code&gt;, &lt;code&gt;query selector&lt;/code&gt;, and &lt;code&gt;form data&lt;/code&gt;, and then returns a form element. But this time we'll use a query selector that doesn't exist to demonstrate how &lt;code&gt;open_browser/2&lt;/code&gt; can be useful in debugging LiveView tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/sample_live_web/registration_live_test.exs&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;SampleLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RegistrationLiveTest&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;SamplLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ConnCase&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LiveViewTest&lt;/span&gt;

  &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"user can see registration form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;conn:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;live&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/user_registration"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;view&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#registration-form"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;open_browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;view&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#wrong_registration-form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;email:&lt;/span&gt; &lt;span class="s2"&gt;"hello@email.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;password:&lt;/span&gt; &lt;span class="s2"&gt;"hello123"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's run the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;ArgumentError&lt;span class="o"&gt;)&lt;/span&gt; expected selector &lt;span class="s2"&gt;"#wrong_registration-form"&lt;/span&gt; to &lt;span class="k"&gt;return &lt;/span&gt;a single element, but got none within:

&amp;lt;main &lt;span class="nv"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"container"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;lt;p &lt;span class="nv"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"alert alert-info"&lt;/span&gt; &lt;span class="nv"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"alert"&lt;/span&gt; phx-click&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"lv:clear-flash"&lt;/span&gt; phx-value-key&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"info"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;lt;/p&amp;gt;&amp;lt;p &lt;span class="nv"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"alert alert-danger"&lt;/span&gt; &lt;span class="nv"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"alert"&lt;/span&gt; phx-click&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"lv:clear-flash"&lt;/span&gt; phx-value-key&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;lt;/p&amp;gt;&amp;lt;form &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"#"&lt;/span&gt; &lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"post"&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"registration-form"&lt;/span&gt; phx-submit&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"save"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;lt;input &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"_csrf_token"&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"hidden"&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"CHwQRnBHAnF7JDQEXQk3AXx8QQ0hZxceYLY-91nA2PLS3bB13Q0HC8CQ"&lt;/span&gt;/&amp;gt;&amp;lt;label &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"registration-form_email"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;Email&amp;lt;/label&amp;gt;&amp;lt;input &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"email-input"&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"user[email]"&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;/&amp;gt;&amp;lt;label &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"registration-form_password"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;Password&amp;lt;/label&amp;gt;&amp;lt;input &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"password-input"&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"user[password]"&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;/&amp;gt;&amp;lt;button &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"submit"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;Save&amp;lt;/button&amp;gt;&amp;lt;/form&amp;gt;&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We notice that our test is failing with an &lt;code&gt;ArgumentError&lt;/code&gt;. In this test, the &lt;code&gt;form/3&lt;/code&gt; function returns a &lt;code&gt;form element&lt;/code&gt; with the query selector &lt;code&gt;#wrong_registration-form&lt;/code&gt;. This &lt;code&gt;form element&lt;/code&gt; is then passed to &lt;code&gt;render/1&lt;/code&gt;, which expects the &lt;code&gt;form element&lt;/code&gt; with the query selector &lt;code&gt;#wrong_registration-form&lt;/code&gt; passed to it as the first argument to return a form element (but it gets none within the LiveView page). The test has made it clear that we are using the wrong query selector.&lt;/p&gt;

&lt;p&gt;Our form template is very short, so it's easy to identify our mistake. Even from the HTML string displayed on the terminal after running the test, it's easy to point out the query selector we should have used.&lt;/p&gt;

&lt;p&gt;Imagine working with larger HTML templates, though. It will be cumbersome to go through the HTML string to try and identify the mistake. Let's examine how &lt;code&gt;open_browser/2&lt;/code&gt; can come in handy in that case.&lt;/p&gt;

&lt;p&gt;First, let's add &lt;code&gt;open_browser/2&lt;/code&gt; after &lt;code&gt;form/3&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/sample_live_web/registration_live_test.exs&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;SampleLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RegistrationLiveTest&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;SamplLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ConnCase&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LiveViewTest&lt;/span&gt;

  &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"user can see registration form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;conn:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;live&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/user_registration"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;view&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#registration-form"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;open_browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;view&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#wrong_registration-form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;email:&lt;/span&gt; &lt;span class="s2"&gt;"hello@email.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;password:&lt;/span&gt; &lt;span class="s2"&gt;"hello123"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;open_browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, we expect the browser to open with the displayed registration form — but apparently, that won't be the case when we run our test. Just like the &lt;code&gt;render/1&lt;/code&gt; function, &lt;code&gt;open_browser/2&lt;/code&gt; expects the &lt;code&gt;form element&lt;/code&gt; with the query selector &lt;code&gt;#wrong_registration-form&lt;/code&gt; passed to it, but it doesn't exist.&lt;/p&gt;

&lt;p&gt;The other option is to debug the LiveView page where the registration form is rendered. In the test, we shall call &lt;code&gt;open_browser/2&lt;/code&gt; and pass the &lt;code&gt;view&lt;/code&gt; returned after invoking &lt;code&gt;live/2&lt;/code&gt; to it. Earlier on, I mentioned that &lt;code&gt;open_browser/2&lt;/code&gt; could either take in a &lt;code&gt;view&lt;/code&gt; or an &lt;code&gt;element&lt;/code&gt;. The option to use an &lt;code&gt;element&lt;/code&gt; is failing due to the wrong &lt;code&gt;query selector&lt;/code&gt;. So using &lt;code&gt;view&lt;/code&gt; can be a better option in our case to help us understand why the test is failing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/sample_live_web/registration_live_test.exs&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;SampleLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RegistrationLiveTest&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;SamplLiveWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ConnCase&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LiveViewTest&lt;/span&gt;

  &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"user can see registration form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;conn:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;live&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/user_registration"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;open_browser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The debug process will be as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the test. This time, we should expect the default browser to be opened and our registration form displayed.&lt;/li&gt;
&lt;li&gt;Use the element inspector in the browser to find the correct form selector.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_JFfw06Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2023-03/browser-inspect.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_JFfw06Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.appsignal.com/images/blog/2023-03/browser-inspect.png" alt="registration page browser inspect" width="880" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the image above, we can see that the form element under the inspect elements section highlighted in blue has a different &lt;code&gt;query selector&lt;/code&gt; (the &lt;code&gt;id&lt;/code&gt; attribute value) specified to the one passed in &lt;code&gt;form/3&lt;/code&gt; in our test. Our test expected the &lt;code&gt;registration_form&lt;/code&gt; query selector. If we now refactor our test and pass the required query selector, it will definitely pass.&lt;/p&gt;

&lt;p&gt;We have seen that we can use &lt;code&gt;open_browser/2&lt;/code&gt; as a debugging tool for our tests. With &lt;code&gt;open_browser/2&lt;/code&gt;, we can easily verify that the correct HTML elements are rendered to users. If the correct &lt;code&gt;view_or_element&lt;/code&gt; passes, the browser opens with the displayed HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitor Your Phoenix LiveView App in Production with AppSignal
&lt;/h2&gt;

&lt;p&gt;If you also want to keep track of your live view performance in production, you can set up LiveView instrumentation in AppSignal. Once you've integrated AppSignal with Phoenix LiveView, you'll be able to monitor incoming HTTP requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.appsignal.com/elixir/integrations/phoenix.html"&gt;Check out our docs for Phoenix LiveView&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;We recommend using &lt;a href="https://blog.appsignal.com/2022/06/20/appsignal-for-phoenix-2-1-automatic-liveview-instrumentation.html"&gt;our automatic LiveView instrumentation&lt;/a&gt; in the majority of cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The introduction of &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveViewTest.html#open_browser/2"&gt;&lt;code&gt;open_browser/2&lt;/code&gt;&lt;/a&gt; has made debugging LiveView tests easy and efficient. In this post, we introduced how to use &lt;code&gt;open_browser/2&lt;/code&gt; when testing to verify that the correct HTML is rendered within your live view.&lt;/p&gt;

&lt;p&gt;Happy debugging!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
    <item>
      <title>How to Cache Locally in Elixir with Nebulex</title>
      <dc:creator>Tracey Onim</dc:creator>
      <pubDate>Tue, 10 Jan 2023 11:43:24 +0000</pubDate>
      <link>https://dev.to/appsignal/how-to-cache-locally-in-elixir-with-nebulex-28i0</link>
      <guid>https://dev.to/appsignal/how-to-cache-locally-in-elixir-with-nebulex-28i0</guid>
      <description>&lt;p&gt;In an Elixir application, you might need to access certain data frequently, which can be costly. The access time involved in retrieving data at every step can cause high latency, or even make the application crash (due to an increased workload on the database).&lt;/p&gt;

&lt;p&gt;Caching is the best technique to store the most frequently accessed data and minimize database data retrieval, improving the overall performance of the application.&lt;/p&gt;

&lt;p&gt;In this post, you'll learn how to use the &lt;a href="https://github.com/cabol/nebulex"&gt;Nebulex&lt;/a&gt; caching toolkit to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cache locally in your Elixir applications&lt;/li&gt;
&lt;li&gt;get familiar with different supported caching solutions/strategies&lt;/li&gt;
&lt;li&gt;see how Nebulex can be a good fit for caching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get going!&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Nebulex?
&lt;/h2&gt;

&lt;p&gt;You've likely already come across some caching options for your existing Elixir application.&lt;/p&gt;

&lt;p&gt;Here, I will introduce you to the Nebulex caching toolkit. Nebulex is an &lt;em&gt;in-memory and distributed caching framework for Elixir&lt;/em&gt; that supports temporarily storing and accessing data in an Elixir application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Nebulex in Your Elixir Application?
&lt;/h2&gt;

&lt;p&gt;Caching can be implemented in many different ways depending on your application use case. You may need a local cache that stores data directly in your application or a distributed cache. Having a cache framework that supports a vast number of caching solutions is a plus. This is where we can leverage the Nebulex caching toolkit.&lt;/p&gt;

&lt;p&gt;Nebulex has a &lt;em&gt;flexible and pluggable architecture based on adapters&lt;/em&gt;. This makes it possible to integrate different caching options and also provides an easy workaround for various &lt;em&gt;caching topologies&lt;/em&gt;, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Partitioned caching:&lt;/strong&gt; distribution of data between multiple nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replicated caching:&lt;/strong&gt; involving multiple servers, where each server has the same copy of data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local caching:&lt;/strong&gt; a single-server cache that resides on a single node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So Nebulex makes it easy for developers to scale their applications beyond a single node (with minimal impact on the code). You only need to choose the adapter that best suits your needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cabol/nebulex"&gt;Check out this in-depth guide on supported caches and their adapters in Nebulex&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now let's look at a real-world example where Nebulex proved useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Fetching and Storing Data from an External API
&lt;/h2&gt;

&lt;p&gt;A while ago, I was working on a payment integration system that involved fetching a transaction from an external API. The fetched transaction was then stored in the database.&lt;/p&gt;

&lt;p&gt;We also had to confirm if the transaction could complete, so we scheduled a job using &lt;a href="https://hexdocs.pm/elixir/GenServer.html"&gt;GenServer&lt;/a&gt;. GenServer sent a request to the external API after a certain period of time to confirm if the transaction was complete and then updated the stored transaction status.&lt;/p&gt;

&lt;p&gt;This approach had the following shortcomings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After every specified period, a request was sent to the external API to confirm if the transaction was completed. This occurred even when all fetched transactions stored in the database were completed.&lt;/li&gt;
&lt;li&gt;After every specified period, a query ran to check if all transactions were complete. This approach resulted in unnecessary trips to the database and was deemed unfit when handling many transactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: Caching Locally with Nebulex
&lt;/h2&gt;

&lt;p&gt;The application was being deployed to a single instance and would frequently access loaded transactions. So to solve the problem stated above, we used Nebulex to locally cache the fetched transactions.&lt;/p&gt;

&lt;p&gt;This approach helped to overcome the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Making unnecessary trips to the external API after every specified period.&lt;/strong&gt; It ensured requests were only made if the cache contained transactions. By default, all cached transactions were incomplete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Making many trips to the database.&lt;/strong&gt; Trips to the database were made only when inserting a fetched transaction and updating its status.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As mentioned earlier, Nebulex supports different caching solutions, one of which is &lt;em&gt;local caching&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We'll now implement Nebulex in an Elixir application when caching locally.&lt;/p&gt;

&lt;p&gt;Let's begin!&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Nebulex to an Elixir Application
&lt;/h2&gt;

&lt;p&gt;First, open the &lt;code&gt;mix.exs&lt;/code&gt; file and add the &lt;code&gt;nebulex&lt;/code&gt; dependency as shown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:nebulex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 2.4"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Nebulex dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix deps.get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation, go ahead and generate your cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix nbx.gen.cache &lt;span class="nt"&gt;-c&lt;/span&gt; Payment.Cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defining a cache module within the application&lt;/li&gt;
&lt;li&gt;Setting up configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll use the name &lt;code&gt;Payment.Cache&lt;/code&gt; to identify our cache (you can name yours as you wish). The command above will generate the &lt;code&gt;Payment.Cache&lt;/code&gt; module defined in &lt;code&gt;lib/payment/cache.ex&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;#lib/payment/cache.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Nebulex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;adapter:&lt;/span&gt; &lt;span class="no"&gt;Nebulex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Adapters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Local&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates the configuration Nebulex will use in &lt;code&gt;config/config.exs&lt;/code&gt;,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;#config/config.exs&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;# When using :shards as backend&lt;/span&gt;
  &lt;span class="c1"&gt;# backend: :shards,&lt;/span&gt;
  &lt;span class="c1"&gt;# GC interval for pushing new generation: 12 hrs&lt;/span&gt;
  &lt;span class="ss"&gt;gc_interval:&lt;/span&gt; &lt;span class="ss"&gt;:timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="c1"&gt;# Max 1 million entries in cache&lt;/span&gt;
  &lt;span class="ss"&gt;max_size:&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;# Max 2 GB of memory&lt;/span&gt;
  &lt;span class="ss"&gt;allocated_memory:&lt;/span&gt; &lt;span class="mi"&gt;2_000_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;# GC min timeout: 10 sec&lt;/span&gt;
  &lt;span class="ss"&gt;gc_cleanup_min_timeout:&lt;/span&gt; &lt;span class="ss"&gt;:timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="c1"&gt;# GC max timeout: 10 min&lt;/span&gt;
  &lt;span class="ss"&gt;gc_cleanup_max_timeout:&lt;/span&gt; &lt;span class="ss"&gt;:timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, open &lt;code&gt;lib/my_app/application.ex&lt;/code&gt; and add the &lt;code&gt;Payment.Cache&lt;/code&gt; to the application supervision tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;#lib/my_app/application.ex&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;

      &lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;

      &lt;span class="c1"&gt;# ...&lt;/span&gt;

    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;Payment.Cache&lt;/code&gt; in the supervision tree starts the Nebulex process when the application starts up.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Nebulex.Cache&lt;/code&gt; Configuration in Elixir
&lt;/h2&gt;

&lt;p&gt;The generated &lt;code&gt;Payment.Cache&lt;/code&gt; module uses the &lt;code&gt;Nebulex.Cache&lt;/code&gt;, a cache abstraction layer controlled by adapters. &lt;code&gt;Nebulex.Cache&lt;/code&gt; expects &lt;code&gt;:otp_app&lt;/code&gt; and &lt;code&gt;:adapter&lt;/code&gt; as options.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:otp_app&lt;/code&gt; points to the Elixir application where &lt;code&gt;nebulex&lt;/code&gt; can find the cache configuration. In our case, &lt;code&gt;:my_app&lt;/code&gt; is specified.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:adapter&lt;/code&gt; - the desired adapter is configured at this point. In our case, &lt;code&gt;Nebulex.Adapters.Local&lt;/code&gt; has been specified. It implements a local generation cache. This is a caching technique based on the age of the cached entries. It involves specifying the object's last modified date in the cache key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The configurations specified in &lt;code&gt;config/config.exs&lt;/code&gt; are specific to &lt;code&gt;Nebulex.Adapters.Local&lt;/code&gt; passed in the &lt;code&gt;:adapter&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;These are some of the options supported by &lt;code&gt;Nebulex.Adapters.Local&lt;/code&gt; via the cache configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;:backend&lt;/code&gt;&lt;/strong&gt; - the storage used for the adapter. The supported forms of storage are &lt;code&gt;:ets&lt;/code&gt; and &lt;code&gt;:shards&lt;/code&gt;, and the default is &lt;code&gt;:ets&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;:gc_interval&lt;/code&gt;&lt;/strong&gt; - interval time in milliseconds for garbage collection to run, which involves deleting the oldest generation and creating a new one. Expects an integer &amp;gt; 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;:max_size&lt;/code&gt;&lt;/strong&gt; specifies the cache limit. Expects an integer &amp;gt; 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;:allocated_memory&lt;/code&gt;&lt;/strong&gt; - maximum size in bytes of the memory allocated for cache generation. Expects an integer &amp;gt; 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;:gc_cleanup_min_timeout&lt;/code&gt;&lt;/strong&gt; - minimum timeout in milliseconds for triggering the next clean-up and memory check. Defaults to 10_000 (10 seconds). Expects an integer &amp;gt; 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;:gc_cleanup_max_timeout&lt;/code&gt;&lt;/strong&gt; - maximum timeout in milliseconds for triggering the next clean-up and memory check. Defaults to 600_000 (10 minutes). Expects an integer &amp;gt; 0.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/nebulex/Nebulex.Adapters.Local.html#module-options"&gt;Read more about supported module options for &lt;code&gt;Nebulex.Adapters.Local&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query with &lt;code&gt;Nebulex.Cache&lt;/code&gt; Callbacks
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Nebulex.Cache&lt;/code&gt; module has callbacks that can be leveraged to perform queries. Let's cache fetched transactions from an external API and manipulate cached entries using callbacks.&lt;/p&gt;

&lt;p&gt;The implementation for manipulating cache entries is in &lt;code&gt;lib/my_app/payment_cache.ex&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;#lib/my_app/payment_cache.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PaymentCache&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="sd"&gt;"""
  context for manipulating cache entries. It involves;
   - Inserting fetched transaction and incomplete transaction into the cache
   - Query for all cached transaction
   - Deleting cached transaction
  """&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Cache&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;insert_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;insert_all_transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;transactions&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status:&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;get_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;all_cached_transactions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;delete_transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;delete_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;delete_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we create a context for manipulating cache entries related to payments. We alias &lt;code&gt;Payment.Cache&lt;/code&gt; (the module generated above in &lt;code&gt;lib/payment/cache.ex&lt;/code&gt;). The module will give us access to the &lt;code&gt;Nebulex.Cache&lt;/code&gt; callbacks.&lt;/p&gt;

&lt;p&gt;The following functions are defined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;insert_transaction/1&lt;/code&gt;&lt;/strong&gt; - takes in a fetched transaction from the external API and inserts it into the cache. &lt;a href="https://hexdocs.pm/nebulex/Nebulex.Cache.html#c:put/3"&gt;Payment.Cache.put/3&lt;/a&gt; is responsible for inserting the transaction into the cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;insert_all_transactions/1&lt;/code&gt;&lt;/strong&gt; - takes in a list of transactions and inserts them into the cache by invoking &lt;a href="https://hexdocs.pm/nebulex/Nebulex.Cache.html#c:put_all/2"&gt;Payment.Cache.put_all/&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;all_cached_transactions/0&lt;/code&gt;&lt;/strong&gt; - fetches all cached entries by invoking &lt;a href="https://hexdocs.pm/nebulex/Nebulex.Cache.html#c:all/2"&gt;Payment.Cache.all/2&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;delete_transaction/1&lt;/code&gt;&lt;/strong&gt; - deletes a cached entry by invoking &lt;a href="https://hexdocs.pm/nebulex/Nebulex.Cache.html#c:delete/2"&gt;Payment.Cache.delete/2&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;get_transaction/1&lt;/code&gt;&lt;/strong&gt; - fetches a cached transaction from the given key by invoking &lt;a href="https://hexdocs.pm/nebulex/Nebulex.Cache.html#c:get/2"&gt;Payment.Cache.get/2&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, open an interactive shell in your terminal, and put the functionality into use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;alias &lt;/span&gt;MyApp.PaymentCache

iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; fetched_transaction &lt;span class="o"&gt;=&lt;/span&gt; %&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;: 1, status: &lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

        %&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;: 1, status: &lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; PaymentCache.insert_transaction&lt;span class="o"&gt;(&lt;/span&gt;fetched_transaction&lt;span class="o"&gt;)&lt;/span&gt;

      :ok

iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; PaymentCache.get_transaction&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;

      %&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;: 1, status: &lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; another_fetched_transaction &lt;span class="o"&gt;=&lt;/span&gt;  %&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;: 2, status: &lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

        %&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;: 2, status: &lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; PaymentCache.insert_transaction&lt;span class="o"&gt;(&lt;/span&gt;another_fetched_transaction&lt;span class="o"&gt;)&lt;/span&gt;

      :ok

iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; PaymentCache.all_cached_transactions&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="c"&gt;# returns the ids of the cached transactions&lt;/span&gt;
      &lt;span class="o"&gt;[&lt;/span&gt;1, 2]

iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; PaymentCache.delete_transaction&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;

      :ok


iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; PaymentCache.all_cached_transactions&lt;span class="o"&gt;()&lt;/span&gt;

      &lt;span class="o"&gt;[&lt;/span&gt;2]

iex &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; PaymentCache.get_transaction&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;

      nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Schedule a Job in Elixir Using Cached Entries
&lt;/h2&gt;

&lt;p&gt;Caching fetched transactions comes in handy when scheduling jobs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;#lib/my_app/payment_scheduler.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PaymentScheduler&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;GenServer&lt;/span&gt;

  &lt;span class="kn"&gt;require&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;

  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PaymentCache&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;GenServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:continue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:cache_incomplete_transactions&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_continue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cache_incomplete_transactions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;insert_all_transaction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;schedule_work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;update_pending_transactions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;schedule_work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;schedule_work&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;update_pending_transactions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;all_cached_transactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PaymentCache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all_cached_transactions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;all_cached_transactions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"All transactions are complete"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;all_cached_transactions&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;

        &lt;span class="n"&gt;complete_transactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;complete_transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_cached_transactions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="no"&gt;Payments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_incomplete_transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;complete_transactions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


        &lt;span class="c1"&gt;# delete cached transactions after update in the db&lt;/span&gt;
        &lt;span class="no"&gt;PaymentCache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;complete_transactions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;complete_transactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached_transactions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="c1"&gt;#fetch all complete transactions from external API&lt;/span&gt;
    &lt;span class="n"&gt;complete_transactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirmed_transactions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;complete_transactions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_cached_transactions&lt;/span&gt;
      &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;insert_all_transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;pending_transactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Transactions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all_pending_transactions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all_pending_transactions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"There are no incomplete transactions in the database"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;pending_transaction&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;PaymentCache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert_all_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pending_transactions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's run through what's going on here.&lt;/p&gt;

&lt;p&gt;When an application starts, &lt;code&gt;init/1&lt;/code&gt; will be invoked. Our &lt;code&gt;init/1&lt;/code&gt; returns &lt;code&gt;{:ok, opts, {:continue, :cache all incomplete transactions}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This will immediately trigger &lt;code&gt;handle_continue(:cache_incomplete_transactions, state)&lt;/code&gt;. Here, all incomplete transactions are fetched from the database and cached. By invoking &lt;code&gt;schedule_work/0&lt;/code&gt;, an updated job is scheduled to take place every 10 seconds.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;handle_continue/2&lt;/code&gt; callback allows us to perform a time-consuming job asynchronously, avoiding race condition cases. For more about &lt;code&gt;handle_continue/2&lt;/code&gt;, I suggest &lt;a href="https://elixirschool.com/blog/til-genserver-handle-continue/"&gt;Sophie DeBenedetto's excellent Elixir School article, 'TIL GenServer’s handle_continue/2'&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;schedule_work/0&lt;/code&gt;, an &lt;code&gt;:update&lt;/code&gt; message notifies the parent process (the GenServer) that there is an update to perform. The &lt;code&gt;:update&lt;/code&gt; message is then handled in the &lt;code&gt;handle_info/2&lt;/code&gt; callback. At this point, we confirm if there are incomplete transactions in the cache (keeping in mind that we only cache incomplete transactions).&lt;/p&gt;

&lt;p&gt;When the cache is empty, that means there are no incomplete transactions. So we skip sending a request to an external API confirming whether the transactions have been completed, and we update their status in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we explored how to use Nebulex for caching data locally in an Elixir application.&lt;/p&gt;

&lt;p&gt;Implementing caching in an Elixir application depends purely on your business use case. When choosing a cache toolkit, make sure it meets your needs.&lt;/p&gt;

&lt;p&gt;Nebulex's cache toolkit supports a vast number of caching solutions and allows you to implement different topologies with minimal impact on the code.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://github.com/cabol/nebulex"&gt;Nebulex's guide&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Happy caching!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, &lt;a href="https://dev.to/elixir-alchemy"&gt;subscribe to our Elixir Alchemy newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
    </item>
  </channel>
</rss>
