<?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: Mohamed ABDELLANI</title>
    <description>The latest articles on DEV Community by Mohamed ABDELLANI (@abdellani).</description>
    <link>https://dev.to/abdellani</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%2F281332%2Fb6d65385-e678-4cd0-8878-4fc0eec28061.jpeg</url>
      <title>DEV Community: Mohamed ABDELLANI</title>
      <link>https://dev.to/abdellani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abdellani"/>
    <language>en</language>
    <item>
      <title>The Journey of a Request in a Raft-Based KV Store</title>
      <dc:creator>Mohamed ABDELLANI</dc:creator>
      <pubDate>Wed, 01 Apr 2026 12:43:14 +0000</pubDate>
      <link>https://dev.to/abdellani/the-journey-of-a-request-in-a-raft-based-kv-store-3d72</link>
      <guid>https://dev.to/abdellani/the-journey-of-a-request-in-a-raft-based-kv-store-3d72</guid>
      <description>&lt;p&gt;Originally published on:&lt;br&gt;
&lt;a href="https://abdellani.dev/posts/2026-04-01-the-journey-of-a-request-in-a-raft-based-kv-store/" rel="noopener noreferrer"&gt;https://abdellani.dev/posts/2026-04-01-the-journey-of-a-request-in-a-raft-based-kv-store/&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Introduction:
&lt;/h1&gt;

&lt;p&gt;This article comes after implementing labs 3 and 4 from the open  MIT 6.584 course. We have a replicated KV server on top of the RAFT protocol, and we want to understand how the server works internally. To do so, I propose to follow all the steps that a request will pass through on the server before a client receives a response.&lt;/p&gt;

&lt;p&gt;The objective of building the KV server on top of the RAFT protocol is to have a fault-tolerant system.&lt;br&gt;
The system is composed of multiple KV server nodes, if some servers fall down, the system should continue serving the clients.&lt;br&gt;
Raft is strongly consistent and can tolerate the failure of f nodes in a system composed of 2 * f + 1 nodes. For example, if we want the system to tolerate the failure of 2 nodes, we’ll need 5 nodes.&lt;/p&gt;

&lt;p&gt;The labs are testing the implementation under different conditions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;an unreliable network that drops packets or delivers packets out of order&lt;/li&gt;
&lt;li&gt;Creating network partitions&lt;/li&gt;
&lt;li&gt;Crashing, restarting servers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The system should work correctly under those conditions.&lt;/p&gt;

&lt;h1&gt;
  
  
  System design
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75nx53yhxgj72mf7d5cv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75nx53yhxgj72mf7d5cv.png" alt="replicate KV design" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each server is composed of 3 layers:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Application layer
&lt;/h2&gt;

&lt;p&gt;It’s responsible for handling the RPC calls coming from the clients, and for handling the logic related to the Key/Value store: read, write operations related to the keys. It communicates directly to the RSM layer and is not aware of the RAFT.&lt;br&gt;
When the client sends a request, this layer will make a submit call to the RSM layer and wait for it to return before responding to the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. RSM layer
&lt;/h2&gt;

&lt;p&gt;The RSM layer is the glue between the Application layer and the raft layer.&lt;br&gt;
When a request is submitted from the application layer, the request will be forwarded to the RAFT layer.&lt;br&gt;
The RSM also monitors the apply Channel. When Raft decides to commit a command, the RSM layer forwards the command to the application layer to execute it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Raft Layer
&lt;/h2&gt;

&lt;p&gt;The Raft consensus protocol layer is responsible for leader elections and log replication.&lt;/p&gt;

&lt;p&gt;When the client sends a request to the server, the server must be a leader or will reject the request.&lt;br&gt;
The initial simple version of the follow consists of the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The application layer will submit the request to the RSM layer and wait for the function call to return before sending an answer to the client.&lt;/li&gt;
&lt;li&gt;The RSM layer will add the client to its internal store and submit the request to the RAFT protocol. If the current server is not the leader, the command will be rejected at this level.&lt;/li&gt;
&lt;li&gt;The Raft will append the command to its log and replicate the command to the other servers. The client must wait until the command is committed and applied before receiving a response.&lt;/li&gt;
&lt;li&gt;If the command is replicated to a majority, and the current term condition is satisfied, the Raft protocol will decide to commit the command.&lt;/li&gt;
&lt;li&gt;The RSM monitoring the apply channel, the channel used by the Raft layer to send the commands that are committed, will forward the command to the application layer by calling DoOp.&lt;/li&gt;
&lt;li&gt;The application layer will update the Key/Value store state. For example, put(x,5) will set the key x to 5 at this stage.&lt;/li&gt;
&lt;li&gt;The RSM receives the DoOp’s result from the application layer, checks its state to find the client waiting on step 2, and communicates the results to the client.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At first glance, this flow looks correct. In practice, it breaks in several ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The server loses the leadership before responding to a client request.
&lt;/h2&gt;

&lt;p&gt;The client submits a request, the leader node receives it, submits it to the RSM, and then loses the leadership immediately. This can happen due to network issues between nodes.&lt;br&gt;
In this situation, the client will be stuck waiting for a response while the command might be removed from the Raft logs since it’s not committed.&lt;br&gt;
To solve this problem, the RSM needs to check periodically with the RAFT layer if the server is still the leader. If the server is no longer the leader, it’ll send an error code “ErrWrongLeader” to the application layer, which in its turn will forward it to the client.&lt;br&gt;
This will tell the client that the targeted server is not the leader, and it has to try submitting its requests to another server.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The leader loses communication with the other nodes.
&lt;/h2&gt;

&lt;p&gt;A client X sends its request to a leader. The leader receives the request and submits it to the Raft layer. The problem is that the leader is no longer able to reach the other nodes to replicate the command in the request.&lt;br&gt;
The other nodes will notice the absence of the leader, run a new election, and select a new leader. The system will serve the other clients, but the initial leader will not notice the changes in the leadership and will assume that it’s still the current leader.&lt;br&gt;
The client X will be stuck waiting for the old leader to replicate and commit the command.&lt;br&gt;
To avoid this situation, different solutions can be implemented.&lt;br&gt;
A leader can monitor its communications with the other nodes, and if it notices that none of the other nodes is reachable for a period of time, it’ll automatically increment the term and downgrade to a follower.&lt;br&gt;
A simpler solution will be to implement a timer on the RSM level. After submitting a command to Raft, the RSM will use that timer to decide if enough time has passed to release the client and ask it to try with other servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The client sends a request, but the network fails.
&lt;/h2&gt;

&lt;p&gt;The failure can happen before the server receives the requests, or when the server processes the request and tries to send a response.&lt;br&gt;
The client might retry again and again, which can lead to overwriting the commands of the other clients.&lt;/p&gt;

&lt;p&gt;One solution is to attach to every request two attributes: a client ID and a request ID.&lt;br&gt;
The server will use those attributes to :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Track the requests' execution progress for each client, and&lt;/li&gt;
&lt;li&gt;Implement a cache&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The server will maintain two data structures.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;latest request ID per client&lt;/li&gt;
&lt;li&gt;latest response per client&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my implementation, I decided to implement the logic for checking and updating the request IDs tracking data structure and the cache on the DoOp function. This will keep RSM layer more flexible to be integrated with a different application layer. Also, we can not rely on the RPC handler to implement that logic. A follower node will only call the DoOp and will never use the RPC handler after processing a committed command. We want every node to keep track of those two data structures.&lt;/p&gt;

&lt;p&gt;An optional optimization will be to check the cache on the RPC handler level before submitting the lower layers.&lt;/p&gt;

&lt;p&gt;When the server receives a request from the client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the request ID is older than the last request ID per that client, the request will be ignored.&lt;/li&gt;
&lt;li&gt;If the request ID equals the latest request ID per that client, return the result from the cache.&lt;/li&gt;
&lt;li&gt;If the request ID is greater than the last request ID per that client, submit the request to RSM, then RAFT, update the two data structures, and then send the results to the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, it’s not necessary to persist the cache or the latest request IDs data structure, but they have to be part of the snapshots.&lt;br&gt;
When a server restarts, after exchanging a few messages with the leader, it’ll learn about the commit index, replay the commands, and rebuild the cache and the request IDs.&lt;br&gt;
If logs are compacted without adding the request IDs and the cache, there will be no way to rebuild them again.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;What looks like a simple request flow might hide many complexities in practice. Most of the complexity is not in Raft itself, but in making the system behave correctly under failures.&lt;/p&gt;

</description>
      <category>go</category>
      <category>distributedsystems</category>
      <category>backend</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Why Raft Can’t Safely Commit Old-Term Entries</title>
      <dc:creator>Mohamed ABDELLANI</dc:creator>
      <pubDate>Mon, 23 Mar 2026 18:00:24 +0000</pubDate>
      <link>https://dev.to/abdellani/why-raft-cant-safely-commit-old-term-entries-370p</link>
      <guid>https://dev.to/abdellani/why-raft-cant-safely-commit-old-term-entries-370p</guid>
      <description>&lt;p&gt;Originally published on:&lt;br&gt;
&lt;a href="https://abdellani.dev/posts/2026-03-23-why-raft-cant-safely-commit-old-term-entries/" rel="noopener noreferrer"&gt;https://abdellani.dev/posts/2026-03-23-why-raft-cant-safely-commit-old-term-entries/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Raft is a consensus algorithm that allows a group of servers to agree on the order of a sequence of commands.&lt;br&gt;&lt;br&gt;
The objective of this article is not to re-explain RAFT, as this work has already been done in several other places, and the RAFT paper gives enough details to implement it. Instead, my focus will be on two problems that I faced when I voluntarily studied the open distributed systems course from MIT (course code 6584, I didn’t enroll in the course officially, and I wasn’t a student of MIT).&lt;/p&gt;

&lt;p&gt;The first issue:&lt;br&gt;&lt;br&gt;
The paper specifies that a node should not commit the commands from previous terms directly. Those commands should be committed indirectly by committing the commands from the leader’s current term.&lt;/p&gt;

&lt;p&gt;At that level, I was not convinced by this constraint, especially after seeing that the initial tests were passing. A simple scenario might be:&lt;br&gt;&lt;br&gt;
Suppose we have 3 servers: A, B, and C.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In term 1, A becomes a leader.
&lt;/li&gt;
&lt;li&gt;A adds a command X and replicates it to B.
&lt;/li&gt;
&lt;li&gt;A crashes before committing the command.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The command X can’t be removed because the C server can’t become a leader in term 2. After all, its log isn’t as up to date as B's. The B server will refuse to vote for C.&lt;br&gt;&lt;br&gt;
The only valid next step is for B to become a leader and continue replicating the command X to C.  &lt;/p&gt;

&lt;p&gt;The answer was in a more complex scenario where the leader replicates its entries through several terms. In such a case, even if the entry is replicated on a majority, it still can be lost. This will break the safety rule that says: if a command is committed, it should not be lost.&lt;/p&gt;

&lt;p&gt;Let’s take a concrete example and see how this can happen.&lt;br&gt;&lt;br&gt;
Suppose we have three servers: A, B, and C.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The  A server  is the leader in term 1
&lt;/li&gt;
&lt;li&gt;The A server receives the first command X, persists it, and crashes before replicating it.
&lt;/li&gt;
&lt;li&gt;The C server becomes a leader in term 2
&lt;/li&gt;
&lt;li&gt;The C server receives a command Y, persists it, and crashes before replicating it
&lt;/li&gt;
&lt;li&gt;The B server can’t make any progress until at least one server reconnects. The system tolerates the loss of one server at most at the same time.
&lt;/li&gt;
&lt;li&gt;The A server reconnects and takes the leadership in term 3 as its log is more updated than B’s log.
&lt;/li&gt;
&lt;li&gt;The A server replicates the command X to B. According to the paper, the command should not be committed.
&lt;/li&gt;
&lt;li&gt;The server A crashes again.
&lt;/li&gt;
&lt;li&gt;The C server reconnects.
&lt;/li&gt;
&lt;li&gt;At this stage, the B server will have command X from term 1, and the C server will have command Y from term 2. This means the C server will take the lead as its last log entry belongs to a more recent term.
&lt;/li&gt;
&lt;li&gt;The C server will force the B server to remove the command X, and replace it with command Y
&lt;/li&gt;
&lt;li&gt;The A server reconnects again. As a follower, it’ll be forced to remove the command X and replace it with Y&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In term 3, the command X was replicated on the majority of servers (A &amp;amp; B). Yet in term 4, the command X was completely erased from logs. So the majority replication alone is not enough to guarantee safety&lt;br&gt;&lt;br&gt;
The following figures illustrate the example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kxxm00eb3eutdc3vzzp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kxxm00eb3eutdc3vzzp.png" alt="raft" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After understanding how a command replicated on a majority can still be lost after a few terms, that’s why the leader should never commit commands from previous terms. &lt;/p&gt;

&lt;p&gt;Now, we can discuss the next question. Raft allows only a leader to consider an entry committed if it is in the current term and replicated on a majority. In term 4, from the previous figure, the server S3 has its entry replicated to a majority, but still can’t commit the command because the latest entry is from term 2. &lt;/p&gt;

&lt;p&gt;As the protocol authorizes only to commit entries from the current term, one valid progress path will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The C server receives a new command Z from the client. This command will be labeled term 4.
&lt;/li&gt;
&lt;li&gt;The C server replicates the new command to a majority (i.e., one additional node to reach a majority)
&lt;/li&gt;
&lt;li&gt;Once the C server confirms that the new command is replicated, it can commit the new command confidently. The first command will be automatically committed, as all the entries before the commit index will be considered as committed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The question that arises at this stage will be: what if the client doesn’t submit a new command? The S3 server will not be able to commit, and the system will not make any progress. &lt;/p&gt;

&lt;p&gt;To avoid falling into a situation where the system freezes for a long time, when a node is promoted to a leader, it adds a new command. A command that doesn’t do anything, a no-operation or  ”NOOP”. It’ll be replicated as any other commands, it’ll have an index, it’ll be committed, and sent on the apply channel. The higher layer doesn’t need to send it to the application layer. The application layer doesn’t need to learn about it or need to know how to interpret it.   &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtxf41ts0twj6s7sddit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtxf41ts0twj6s7sddit.png" alt="noop" width="650" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The figure shows how the leader replicates the NOOP to the other node. Since NOOP’s term is the same as the current term of the leader, the leader can commit without facing the risk of losing the command in the future.&lt;/p&gt;

&lt;p&gt;In conclusion, this shows why RAFT restricts to the current term, and why the NOOP commands are a practical way for a leader to safely make progress.&lt;/p&gt;

&lt;p&gt;References:&lt;br&gt;&lt;br&gt;
&lt;a href="http://nil.csail.mit.edu/6.5840/2025/papers/raft-extended.pdf" rel="noopener noreferrer"&gt;http://nil.csail.mit.edu/6.5840/2025/papers/raft-extended.pdf&lt;/a&gt; &lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>go</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Exploring Prometheus Code - part 1 (personal notes)</title>
      <dc:creator>Mohamed ABDELLANI</dc:creator>
      <pubDate>Tue, 19 Dec 2023 08:20:27 +0000</pubDate>
      <link>https://dev.to/abdellani/exploring-prometheus-code-part-1-personal-notes-4f0a</link>
      <guid>https://dev.to/abdellani/exploring-prometheus-code-part-1-personal-notes-4f0a</guid>
      <description>&lt;p&gt;I recently started learning the Go language and part of the process was to explore the code of popular open-source projects. Prometheus is one of the projects that I selected. It's a popular project with 848 contributors and more than 51k starts.&lt;/p&gt;

&lt;p&gt;The objectives of this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;understand how the code is organized in the filesystem,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;explore some of the popular packages used on this project,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;understand how the different modules are loaded&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;understand how the communication between the modules is implemented.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I started with the &lt;code&gt;/cmd&lt;/code&gt; folder, as it stores the main function that will be called when we run Prometheus from the CLI.&lt;/p&gt;

&lt;p&gt;Before reading that function, it'll be useful to learn about the following packages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/alecthomas/kingpin" rel="noopener noreferrer"&gt;kingpin&lt;/a&gt;: to parse the command line arguments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/oklog/run" rel="noopener noreferrer"&gt;run&lt;/a&gt;: A universal mechanism to manage goroutine lifecycles (from the repo description). &lt;a href="https://blog.gopheracademy.com/advent-2017/run-group/" rel="noopener noreferrer"&gt;check reference&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pkg.go.dev/context" rel="noopener noreferrer"&gt;context&lt;/a&gt;: to share state between function/goroutines. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-contexts-in-go" rel="noopener noreferrer"&gt;check reference&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main function does the following things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;parsing arguments and load configuration and populate the data structure &lt;code&gt;flagConfig\&lt;/code&gt; (using kingpin package)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"config.file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Prometheus configuration file path."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"prometheus.yml"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringVar&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;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"web.listen-address"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Address to listen on for UI, API, and telemetry."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:9090"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringVar&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;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;checking the consistency between the configurations
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;agentMode&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverOnlyFlags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"The following flag(s) can not be used in agent mode: %q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverOnlyFlags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Launching the different modules (using run package)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Scrape manager.&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;reloadReady&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;
                &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;scrapeManager&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="n"&gt;discoveryManagerScrape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SyncCh&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="n"&gt;level&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Scrape manager stopped"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;level&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="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Stopping scrape manager..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;scrapeManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&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="p"&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 project is composed of several modules, each module is stored in a folder at the root level of the project. (&lt;a href="https://github.com/prometheus/prometheus/blob/main/documentation/internal_architecture.md" rel="noopener noreferrer"&gt;internal architecture&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I started with the two modules: &lt;code&gt;discovery&lt;/code&gt; and &lt;code&gt;scrape&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;discovery&lt;/code&gt; implements the service discovery (&lt;code&gt;ScrapeDiscoveryManager&lt;/code&gt;) that collects the target groups and sends them to &lt;code&gt;scrape&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;scrape&lt;/code&gt; implement the scrape manager (&lt;code&gt;ScrapeManager&lt;/code&gt;) that controls the scraper pools (check &lt;a href="https://github.com/prometheus/prometheus/blob/main/documentation/internal_architecture.md" rel="noopener noreferrer"&gt;internal architecture&lt;/a&gt; for details).&lt;/p&gt;

&lt;p&gt;Each of those two managers will run in a different go-routine.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ScrapeDiscoveryManager&lt;/code&gt; will send the target groups to the &lt;code&gt;ScrapeManager&lt;/code&gt; through a sync channel (SyncCh). This channel will be passed on the creation time in the main function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c"&gt;// When the scrape manager receives a new targets list&lt;/span&gt;
                &lt;span class="c"&gt;// it needs to read a valid config for each job.&lt;/span&gt;
                &lt;span class="c"&gt;// It depends on the config being in sync with the discovery manager so&lt;/span&gt;
                &lt;span class="c"&gt;// we wait until the config is fully loaded.&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;reloadReady&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;

                &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;scrapeManager&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="n"&gt;discoveryManagerScrape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SyncCh&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the &lt;code&gt;ScrapeManager#Run&lt;/code&gt; runs a loop that will watch for updates from the &lt;code&gt;ScrapeDiscoveryManager&lt;/code&gt; and update the scraper pools accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// tsets is returned channel from discoveryManagerScrape.SyncCh()&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsets&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;][]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;targetgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reloader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&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;ts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;tsets&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="c"&gt;// New Updates!!&lt;/span&gt;
            &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updateTsets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;select&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;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;triggerReload&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{}&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="c"&gt;// trigger reload&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graceShut&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the &lt;code&gt;reloader&lt;/code&gt; will &lt;code&gt;reload&lt;/code&gt; that will create/update the scraper pools&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;reloader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reloadIntervalDuration&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graceShut&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;triggerReload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Update the scraper pools&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graceShut&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's return to &lt;code&gt;ScrapeDiscoveryManager&lt;/code&gt; , to understand how it works we need to explore the data structure (some details are omitted).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//discovery/manager.go&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Manager&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;targets&lt;/span&gt; &lt;span class="c"&gt;//  contains the list of all the targets&lt;/span&gt;
    &lt;span class="n"&gt;providers&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Provider&lt;/span&gt; 
    &lt;span class="n"&gt;syncCh&lt;/span&gt; &lt;span class="c"&gt;// the communication channel to send targets to ScrapeManager&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;      &lt;span class="n"&gt;Discoverer&lt;/span&gt;
&lt;span class="c"&gt;//....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Discoverer&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Run hands a channel to the discovery provider (Consul, DNS, etc.) through which&lt;/span&gt;
    &lt;span class="c"&gt;// it can send updated target groups.&lt;/span&gt;
    &lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;targetgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&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 manager has a list of providers, each one includes a discoverer that'll periodically create lists of targets and send them to the discovery manager.&lt;/p&gt;

&lt;p&gt;We can see that on the ScrapeDiscoveryManager\'s &lt;code&gt;startProvider&lt;/code&gt; an &lt;code&gt;update&lt;/code&gt; channel is created and passed to the discoverer and the &lt;code&gt;updater&lt;/code&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;startProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Starting provider"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"subs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithCancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;updates&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;targetgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&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="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each discoverer implements the function &lt;code&gt;refresh&lt;/code&gt; function that will collect data and send it. (Some discoverers interact with the provider through &lt;code&gt;refresh/refresh&lt;/code&gt; ).&lt;/p&gt;

&lt;p&gt;The discoverer's logic can be summarized as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt; &lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Discovery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;targetgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Canceled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Unable to refresh target groups"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"err"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;select&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;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;tgs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now on &lt;code&gt;ScrapeDiscoveryManager&lt;/code&gt;, when &lt;code&gt;updater&lt;/code&gt; receives new updates from the discoverer, it'll&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;update the statistics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;update the list of targets&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;trigger send: unblock the &lt;code&gt;sender&lt;/code&gt; (check &lt;code&gt;ScrapeDiscoveryManager&lt;/code&gt;'&lt;code&gt;Run&lt;/code&gt; below) loop to send the update to the &lt;code&gt;ScrapeManager&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;updater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;targetgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Ensure targets from this provider are cleaned up.&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cleaner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;tgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;receivedUpdates&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithLabelValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Inc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="c"&gt;//...&lt;/span&gt;
            &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updateGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poolKey&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;tgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RUnlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="k"&gt;select&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;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;triggerSend&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{}&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ScrapeDiscoveryManager&lt;/code&gt; 's Run method will run the &lt;code&gt;sender&lt;/code&gt; function which is responsible for sending updates to the &lt;code&gt;ScrapeManager&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// send updates to ScrapeManager&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelDiscoverers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTicker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updatert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="c"&gt;// Some discoverers send updates too often, so we throttle these with the ticker.&lt;/span&gt;
            &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;triggerSend&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;sentUpdates&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithLabelValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Inc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;select&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;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syncCh&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allGroups&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                  &lt;span class="c"&gt;//...&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diagram below summarizes the communication flow between the discoverer to the scrape manager&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw15faeebctxditc02tu5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw15faeebctxditc02tu5.png" alt="Communication between ScrapeDiscoveryManager and ScrapeManager" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>go</category>
      <category>beginners</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Github Actions to run Rubocop and RSpec tests on Rails with Postgres</title>
      <dc:creator>Mohamed ABDELLANI</dc:creator>
      <pubDate>Mon, 27 Jan 2020 09:29:56 +0000</pubDate>
      <link>https://dev.to/abdellani/github-actions-to-run-rubocop-and-rspec-tests-on-rails-with-postgres-47i</link>
      <guid>https://dev.to/abdellani/github-actions-to-run-rubocop-and-rspec-tests-on-rails-with-postgres-47i</guid>
      <description>&lt;p&gt;Hi,&lt;/p&gt;

&lt;p&gt;As my first post in this community, I decided to share my experience when I was testing Github's actions. I hope that you'll find it useful.&lt;/p&gt;

&lt;h1&gt;
  
  
  Objectif
&lt;/h1&gt;

&lt;p&gt;My goal was to define a workflow composed of three steps for testing a rails application. These steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using &lt;a href="https://github.com/rubocop-hq/rubocop" rel="noopener noreferrer"&gt;Rubocup&lt;/a&gt;, the code will be statically analyzed against &lt;a href="https://rubystyle.guide/" rel="noopener noreferrer"&gt;the Ruby style guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If the code passes the previous step, the RSpec tests are triggered to check if the new committed code didn't break anything somewhere else.&lt;/li&gt;
&lt;li&gt;Finally, if all tests pass, you will get a report generated using &lt;a href="https://github.com/colszowka/simplecov" rel="noopener noreferrer"&gt;SimpleCov&lt;/a&gt; to see how well the tests covered the application's code&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The demo application
&lt;/h1&gt;

&lt;p&gt;You can find the code that I used for the demo in this &lt;a href="https://github.com/abdellani/rails-github-actions" rel="noopener noreferrer"&gt; github repository&lt;/a&gt;. The application is a pretty simple, one "Post" model with two attributed "title" and "content". Postgres is the database to store the data.&lt;/p&gt;

&lt;p&gt;if you check &lt;code&gt;config/database.yml&lt;/code&gt;, you'll find that I have solely edited the section related to the testing database as I didn't need to run the application in the development environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;#config/database.yml&lt;/span&gt;
&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
&lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rails-github-action_test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Setting up the workflow
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Create &lt;code&gt;.github/workflows&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The first step is to create a folder called &lt;code&gt;.github/workflows&lt;/code&gt; in the root of your project folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the file that will describe the workflow
&lt;/h2&gt;

&lt;p&gt;In this file, you write all the details need to run the workflow. The extension must &lt;code&gt;.yml&lt;/code&gt; or &lt;code&gt;.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define the environment variables
&lt;/h3&gt;

&lt;p&gt;This section is not required but it makes the reconfiguration of the workflow for other contexts easier.  This section should start with the keyword &lt;code&gt;env&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;RUBY_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.4&lt;/span&gt;
  &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RUBY_VERSION&lt;/code&gt; will used to specify which version of ruby to use in the environment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSTGRES_USER&lt;/code&gt;, &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; and &lt;code&gt;POSTGRES_DB&lt;/code&gt; are the database related information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Define the name of the workflow and when it should be triggered
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rails tests&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;job1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;job2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is the name of the workflow that will appear in the Github Actions UI.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mfli3309q413e3k42g0.png" alt="Workflow name" width="585" height="265"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;on&lt;/code&gt; specify what are the events that will trigger the workflow. In this example, it'll be triggered on &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;pull_request&lt;/code&gt; events.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jobs&lt;/code&gt; will contain all the &lt;code&gt;tasks&lt;/code&gt; to be defined in the workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Describe jobs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Rubocop test
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rubocop-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rubocop&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-ruby@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ruby-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.RUBY_VERSION }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Rubocop&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gem install rubocop&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check code&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rubocop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is the name of the task.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runs-on&lt;/code&gt; specifies which operating system will be used to run the task. In addition to &lt;code&gt;ubuntu-18.04&lt;/code&gt;, you can use &lt;code&gt;macOS&lt;/code&gt; and &lt;code&gt;Windows Server 2019&lt;/code&gt;. For more details, you can check this &lt;a href="https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idruns-on" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In the section &lt;code&gt;steps&lt;/code&gt;, we'll define the instructions that will run in the operating system in order to prepare it, and make it run the tests. In this example :

&lt;ul&gt;
&lt;li&gt;We call the action &lt;a href="https://github.com/actions/checkout/tree/releases/v1" rel="noopener noreferrer"&gt;actions/checkout@v1&lt;/a&gt; which necessary to check out the repository.&lt;/li&gt;
&lt;li&gt;We call the action &lt;a href="https://github.com/actions/setup-ruby" rel="noopener noreferrer"&gt;actions/setup-ruby@v1&lt;/a&gt; to install ruby,&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;run&lt;/code&gt; to run the command to install rubocop &lt;code&gt;gem install rubocop&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;And finally, we run the test using &lt;code&gt;rubocop&lt;/code&gt;.
you can learn more about actions &lt;a href="https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-actions" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;If the code passes the test, you'll get something like &lt;a href="https://github.com/abdellani/rails-github-actions/runs/369175310?check_suite_focus=true" rel="noopener noreferrer"&gt;this&lt;/a&gt; :&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fk8otw36fw2bc1hkpq3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5fk8otw36fw2bc1hkpq3.png" alt="Screenshot 2" width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  RSpec tests
&lt;/h4&gt;

&lt;p&gt;For the RSpect test, We use a very similar code. The first difference is that we used &lt;code&gt;needs&lt;/code&gt; to precise that the task should run only after the task &lt;code&gt;rubocop-test&lt;/code&gt; is finished and passed successfully.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="s"&gt;...&lt;/span&gt;
   &lt;span class="s"&gt;rspec-test&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RSpec&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rubocop-test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-ruby@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ruby-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.RUBY_VERSION }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install postgres client&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo apt-get install libpq-dev&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;gem install bundler&lt;/span&gt;
          &lt;span class="s"&gt;bundler install&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create database&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;bundler exec rails db:create RAILS_ENV=test&lt;/span&gt;
          &lt;span class="s"&gt;bundler exec rails db:migrate RAILS_ENV=test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundler exec rake&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;As &lt;code&gt;libpq-dev&lt;/code&gt; is a required package to allow the rails application to communicate with &lt;code&gt;Postgres&lt;/code&gt;. We added a step to install it using &lt;code&gt;apt-get install libpq-dev&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To create the database and the tables, we defined the step &lt;code&gt;Create database&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To run the test, we used the command &lt;code&gt;bundler exec rake&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this level, if we run the tests they'll fail. The reason is that we didn't create database service yet. We need to define a new section &lt;code&gt;services&lt;/code&gt; inside the &lt;code&gt;rspec-test&lt;/code&gt; job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;image&lt;/code&gt; is the docker image name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;port&lt;/code&gt; maps the local ports to the container's ports.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="na"&gt;rspec-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RSpec&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rubocop-test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SampleCov report
&lt;/h3&gt;

&lt;p&gt;Now, we add the following step in order to upload the SimpleCov report from the testing environment and make it available to developers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload coverage results&lt;/span&gt;    
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@master&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;coverage-report&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;coverage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;we are using the action &lt;a href="https://github.com/actions/upload-artifact" rel="noopener noreferrer"&gt;actions/upload-artifact@master&lt;/a&gt; to upload the report.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt; is the path of the folder/file to upload&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is the name of the downloadable file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your code passes the tests, you'll be able to download the report from the link in the upper-right corner.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff88xhob772qo0mc80gsy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff88xhob772qo0mc80gsy.png" alt="Screenshot" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check the full code in this &lt;a href="https://github.com/abdellani/rails-github-actions/blob/development/.github/workflows/workflow.yml" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions" rel="noopener noreferrer"&gt;https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.knapsackpro.com/2019/how-to-run-rspec-on-github-actions-for-ruby-on-rails-app-using-parallel-jobs" rel="noopener noreferrer"&gt;https://docs.knapsackpro.com/2019/how-to-run-rspec-on-github-actions-for-ruby-on-rails-app-using-parallel-jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jer-k.github.io/github-action-rspec-simplecov/" rel="noopener noreferrer"&gt;https://jer-k.github.io/github-action-rspec-simplecov/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>github</category>
      <category>docker</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
