<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[My Notes]]></title><description><![CDATA[My Notes]]></description><link>https://ermanimer.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 26 Jun 2026 14:18:04 GMT</lastBuildDate><atom:link href="https://ermanimer.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Developing a Kong Plugin with Go]]></title><description><![CDATA[When trying to develop a custom Kong plugin with Go, I couldn't find any working examples for the latest Kong version (3.9.0 at the time of writing). This post demonstrates a simple, working Go plugin example to build upon. For reference and further ...]]></description><link>https://ermanimer.hashnode.dev/developing-a-kong-plugin-with-go</link><guid isPermaLink="true">https://ermanimer.hashnode.dev/developing-a-kong-plugin-with-go</guid><category><![CDATA[Go Language]]></category><category><![CDATA[Kong API Gateway]]></category><category><![CDATA[Custom Plugin]]></category><dc:creator><![CDATA[Erman İmer]]></dc:creator><pubDate>Thu, 20 Mar 2025 08:07:50 GMT</pubDate><content:encoded><![CDATA[<p>When trying to develop a custom Kong plugin with Go, I couldn't find any working examples for the latest Kong version (3.9.0 at the time of writing). This post demonstrates a simple, working Go plugin example to build upon. For reference and further reading, visit the official documentation's <a target="_blank" href="https://docs.konghq.com/gateway/latest/plugin-development/">Develop Custom Plugins</a> <a target="_blank" href="https://docs.konghq.com/gateway/latest/plugin-development/">section.</a></p>
<h2 id="heading-setup-basic-kong-configuration">Setup Basic Kong Configuration</h2>
<p>Create the <code>kong.yml</code> and <code>docker-compose.yml</code> files. This example uses <a target="_blank" href="https://reqres.in/api/users/1"><code>https://reqres.in/api/users/1</code></a> as a mock service. The goal is to forward requests sent to <code>test-service</code>'s <code>test-route</code> to the mock service URL and return the response.</p>
<h3 id="heading-kongyml">kong.yml</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">_format_version:</span> <span class="hljs-string">"3.0"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test-service</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">https://reqres.in/api/users/1</span>
    <span class="hljs-attr">routes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test-route</span>
        <span class="hljs-attr">paths:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">/</span>
        <span class="hljs-attr">strip_path:</span> <span class="hljs-literal">false</span>
</code></pre>
<h3 id="heading-docker-composeyml">docker-compose.yml</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">kong-go-plugin</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">kong:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">kong</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">kong:latest</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./kong.yml:/kong/declarative/kong.yml</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_DATABASE=off</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_DECLARATIVE_CONFIG=/kong/declarative/kong.yml</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PROXY_ACCESS_LOG=/dev/stdout</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_ADMIN_ACCESS_LOG=/dev/stdout</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PROXY_ERROR_LOG=/dev/stderr</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_ADMIN_ERROR_LOG=/dev/stderr</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_ADMIN_LISTEN=0.0.0.0:8001</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_ADMIN_GUI_URL=http://localhost:8002</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8000:8000"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8443:8443"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8001:8001"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8444:8444"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8002:8002"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8445:8445"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8003:8003"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8004:8004"</span>
</code></pre>
<h2 id="heading-deploy-basic-kong-configuration">Deploy Basic Kong Configuration</h2>
<p>Start Kong with this command:</p>
<pre><code class="lang-bash">docker compose up -d
</code></pre>
<p>Test the setup by sending a request to Kong:</p>
<pre><code class="lang-bash">curl http://localhost:8000
</code></pre>
<p>The mock service should respond with:</p>
<pre><code class="lang-bash">{<span class="hljs-string">"data"</span>:{<span class="hljs-string">"id"</span>:1,<span class="hljs-string">"email"</span>:<span class="hljs-string">"george.bluth@reqres.in"</span>,<span class="hljs-string">"first_name"</span>:<span class="hljs-string">"George"</span>,<span class="hljs-string">"last_name"</span>:<span class="hljs-string">"Bluth"</span>,<span class="hljs-string">"avatar"</span>:<span class="hljs-string">"https://reqres.in/img/faces/1-image.jpg"</span>},<span class="hljs-string">"support"</span>:{<span class="hljs-string">"url"</span>:<span class="hljs-string">"https://contentcaddy.io?utm_source=reqres&amp;utm_medium=json&amp;utm_campaign=referral"</span>,<span class="hljs-string">"text"</span>:<span class="hljs-string">"Tired of writing endless social media content? Let Content Caddy generate it for you."</span>}}
</code></pre>
<h2 id="heading-create-the-go-plugin">Create the Go Plugin</h2>
<p>Create a file named <code>plugin.go</code> that adds a custom header to the response:</p>
<pre><code class="lang-bash">go mod init test-plugin
go get github.com/Kong/go-pdk
go get github.com/Kong/go-pdk/server
</code></pre>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"github.com/Kong/go-pdk"</span>
    <span class="hljs-string">"github.com/Kong/go-pdk/server"</span>
)

<span class="hljs-keyword">const</span> (
    version  = <span class="hljs-string">"0.0.1"</span>
    priority = <span class="hljs-number">1</span>
)

<span class="hljs-keyword">type</span> Config <span class="hljs-keyword">struct</span> {
    Message <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"message"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">New</span><span class="hljs-params">()</span> <span class="hljs-title">interface</span></span>{} {
    <span class="hljs-keyword">return</span> &amp;Config{}
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *Config)</span> <span class="hljs-title">Access</span><span class="hljs-params">(kong *pdk.PDK)</span></span> {
    kong.Response.AddHeader(<span class="hljs-string">"X-Test-Header"</span>, c.Message)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    _ = server.StartServer(New, version, priority)
}
</code></pre>
<p>This plugin accepts a <code>message</code> configuration parameter and adds it as a value in the <code>X-My-Header</code> response header. The <code>Access</code> function is called during Kong's request processing lifecycle. The plugin version ("0.0.1") and priority (1) are defined as constants - Kong executes plugins in order from highest to lowest priority. For more advanced features, check the official documentation's <a target="_blank" href="https://docs.konghq.com/gateway/latest/plugin-development/pluginserver/go/">Write Plugins in Go</a> section.</p>
<h2 id="heading-compile-the-plugin">Compile the Plugin</h2>
<p>Build the plugin, cross-compiling is necessary unless you are developing on Linux, since Kong runs in a Linux container.</p>
<pre><code class="lang-bash">go mod tidy
GOOS=linux go build -o test-plugin plugin.go
</code></pre>
<h2 id="heading-update-kong-configuration">Update Kong Configuration</h2>
<p>Update <code>kong.yml</code> to add the plugin to the test service:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">_format_version:</span> <span class="hljs-string">"3.0"</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test-service</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">https://reqres.in/api/users/1</span>
    <span class="hljs-attr">routes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test-route</span>
        <span class="hljs-attr">paths:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">/</span>
        <span class="hljs-attr">strip_path:</span> <span class="hljs-literal">false</span>
    <span class="hljs-attr">plugins:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test-plugin</span>
        <span class="hljs-attr">config:</span>
          <span class="hljs-attr">message:</span> <span class="hljs-string">"Test message!"</span>

<span class="hljs-attr">plugins:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test-plugin</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>Modify <code>docker-compose.yml</code> to copy the compiled plugin into the Kong container and set environment variables to enable and configure the plugin:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">kong-go-plugin</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">kong:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">kong</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">kong:latest</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./kong.yml:/kong/declarative/kong.yml</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./test-plugin:/usr/local/bin/test-plugin</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_DATABASE=off</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_DECLARATIVE_CONFIG=/kong/declarative/kong.yml</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PROXY_ACCESS_LOG=/dev/stdout</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_ADMIN_ACCESS_LOG=/dev/stdout</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PROXY_ERROR_LOG=/dev/stderr</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_ADMIN_ERROR_LOG=/dev/stderr</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_ADMIN_LISTEN=0.0.0.0:8001</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_ADMIN_GUI_URL=http://localhost:8002</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PLUGINS=test-plugin</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PLUGINSERVER_NAMES=test-plugin</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PLUGINSERVER_TEST_PLUGIN_SOCKET=/usr/local/kong/test-plugin.socket</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PLUGINSERVER_TEST_PLUGIN_START_CMD=/usr/local/bin/test-plugin</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KONG_PLUGINSERVER_TEST_PLUGIN_QUERY_CMD=/usr/local/bin/test-plugin</span> <span class="hljs-string">-dump</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8000:8000"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8443:8443"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8001:8001"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8444:8444"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8002:8002"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8445:8445"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8003:8003"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"8004:8004"</span>
</code></pre>
<h2 id="heading-deploy-with-the-plugin">Deploy with the Plugin</h2>
<p>Restart the container with the updated configuration:</p>
<pre><code class="lang-bash">docker compose down
docker compose up -d
</code></pre>
<p>Test the plugin by sending a request with verbose output to see the custom header:</p>
<pre><code class="lang-bash">curl -v http://localhost:8000
</code></pre>
<p>The response should contain the custom header along with the data:</p>
<pre><code class="lang-bash">...
&lt; X-Test-Header: Test message!
...
{<span class="hljs-string">"data"</span>:{<span class="hljs-string">"id"</span>:1,<span class="hljs-string">"email"</span>:<span class="hljs-string">"george.bluth@reqres.in"</span>,<span class="hljs-string">"first_name"</span>:<span class="hljs-string">"George"</span>,<span class="hljs-string">"last_name"</span>:<span class="hljs-string">"Bluth"</span>,<span class="hljs-string">"avatar"</span>:<span class="hljs-string">"https://reqres.in/img/faces/1-image.jpg"</span>},<span class="hljs-string">"support"</span>:{<span class="hljs-string">"url"</span>:<span class="hljs-string">"https://contentcaddy.io?utm_source=reqres&amp;utm_medium=json&amp;utm_campaign=referral"</span>,<span class="hljs-string">"text"</span>:<span class="hljs-string">"Tired of writing endless social media content? Let Content Caddy generate it for you."</span>}}
</code></pre>
<p>The final project layout:</p>
<pre><code class="lang-bash">.
├── docker-compose.yml
├── go.mod
├── go.sum
├── kong.yml
├── plugin.go
└── test-plugin
</code></pre>
<p>This guide demonstrates developing a custom Kong plugin with Go. This simple example can serve as a starting point for more complex plugin development.</p>
]]></content:encoded></item><item><title><![CDATA[Connecting to Azure IoT Hub with Go and MQTT]]></title><description><![CDATA[In this article, I want to show you how to connect a Go application to Azure IoT Hub using the MQTT protocol. We'll implement a secure connection with TLS (Transport Layer Security) to encrypt our data and use SAS (Shared Access Signature) tokens for...]]></description><link>https://ermanimer.hashnode.dev/connecting-to-azure-iot-hub-with-go-and-mqtt</link><guid isPermaLink="true">https://ermanimer.hashnode.dev/connecting-to-azure-iot-hub-with-go-and-mqtt</guid><category><![CDATA[Azure IoT Hub]]></category><category><![CDATA[mqtt]]></category><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Erman İmer]]></dc:creator><pubDate>Wed, 26 Feb 2025 20:03:22 GMT</pubDate><content:encoded><![CDATA[<p>In this article, I want to show you how to connect a Go application to Azure IoT Hub using the MQTT protocol. We'll implement a secure connection with TLS (Transport Layer Security) to encrypt our data and use SAS (Shared Access Signature) tokens for authentication.</p>
<p>I assume you already have an Azure IoT Hub with at least one device added to it. You'll need three pieces of information to use this code:</p>
<ul>
<li><p>Your IoT Hub name</p>
</li>
<li><p>Your device ID</p>
</li>
<li><p>Your device key (the primary key that was automatically generated when you added the device)</p>
</li>
</ul>
<p>For implementing MQTT connectivity, we'll use the <a target="_blank" href="https://github.com/eclipse-paho/paho.mqtt.golang">Eclipse Paho MQTT Go client</a> library.</p>
<h2 id="heading-generating-sas-shared-access-signature-token">Generating SAS (Shared Access Signature) Token</h2>
<p>The <code>generateSASToken</code> function is a crucial part of our implementation. It creates a secure authentication token that Azure IoT Hub will recognize and accept. The function takes four inputs: hub name, device ID, device key, and an expiration duration.</p>
<p>The function works by creating a unique signature from the resource URI (combining hub name and device ID) and a timestamp. This signature is then hashed using HMAC-SHA256 with the device key. The resulting token allows our device to authenticate with Azure IoT Hub while ensuring the connection remains secure for the specified duration.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">generateSASToken</span><span class="hljs-params">(hubName, deviceID, deviceKey <span class="hljs-keyword">string</span>, expiration time.Duration)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-comment">// create the timestamp for expiration</span>
    timestamp := time.Now().Add(expiration).Unix()
    timestampString := strconv.FormatInt(timestamp, <span class="hljs-number">10</span>)

    <span class="hljs-comment">// create the resource uri</span>
    uri := hubName + <span class="hljs-string">"/devices/"</span> + deviceID
    escapedURI := url.QueryEscape(uri)

    <span class="hljs-comment">// create the signature</span>
    signature := escapedURI + <span class="hljs-string">"\n"</span> + timestampString

    <span class="hljs-comment">// hash the signature</span>
    decodedKey, _ := base64.StdEncoding.DecodeString(deviceKey)
    hash := hmac.New(sha256.New, decodedKey)
    hash.Write([]<span class="hljs-keyword">byte</span>(signature))
    encryptedSignature := hash.Sum(<span class="hljs-literal">nil</span>)
    decodedSignature := base64.StdEncoding.EncodeToString(encryptedSignature)
    escapedSignature := url.QueryEscape(decodedSignature)

    <span class="hljs-comment">// create and return the token</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"SharedAccessSignature sr="</span> + escapedURI + <span class="hljs-string">"&amp;sig="</span> + escapedSignature + <span class="hljs-string">"&amp;se="</span> + timestampString
}
</code></pre>
<h2 id="heading-configuring-and-creating-the-mqtt-client">Configuring and Creating the MQTT Client</h2>
<p>In this configuration, we're establishing a secure connection to Azure IoT Hub using TLS over port 8883. The broker URL is formatted with the "ssl://" prefix to indicate secure communication. For authentication, Azure IoT Hub requires a specific username format combining the hub name and device ID with a slash separator (<code>hubName + "/" + deviceID</code>), while the SAS token we generated serves as the password. The TLS configuration is set to use the system's trusted certificates to verify Azure's server identity, with InsecureSkipVerify set to false to ensure proper certificate validation.</p>
<pre><code class="lang-go"><span class="hljs-comment">// create the client options</span>
brokerURL := <span class="hljs-string">"ssl://"</span> + hubName + <span class="hljs-string">":8883"</span>

username := hubName + <span class="hljs-string">"/"</span> + deviceID

sasToken := generateSASToken(hubName, deviceID, deviceKey, tokenExpiration)

certPool, err := x509.SystemCertPool()
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    slog.Error(<span class="hljs-string">"failed to load system certificate pool"</span>, <span class="hljs-string">"error"</span>, err)
    <span class="hljs-keyword">return</span>
}

tlsConfig := &amp;tls.Config{
    RootCAs:            certPool,
    MinVersion:         tls.VersionTLS12,
    InsecureSkipVerify: <span class="hljs-literal">false</span>,
    ServerName:         hubName,
}

opts := mqtt.NewClientOptions().
    AddBroker(brokerURL).
    SetClientID(deviceID).
    SetUsername(username).
    SetPassword(sasToken).
    SetTLSConfig(tlsConfig)

<span class="hljs-comment">// create the client</span>
client := mqtt.NewClient(opts)
</code></pre>
<h2 id="heading-connecting-to-azure-iot-hub">Connecting to Azure IoT Hub</h2>
<p>After configuring our client options, the next step is to establish the connection with Azure IoT Hub's MQTT broker. Here, we initiate the connection with <code>client.Connect()</code>, which returns a token that we can use to track the operation's status. The <code>WaitTimeout()</code> method blocks execution until the connection is established or the specified timeout is reached. We then check for any errors that might have occurred during the connection process. The <code>defer</code> statement ensures our client disconnects properly when the function exits, even if an error occurs later. Upon successful connection, we log a message to confirm we've connected to the broker.</p>
<pre><code class="lang-go"><span class="hljs-comment">// connect to the broker</span>
token := client.Connect()
token.WaitTimeout(connectTimeout)
<span class="hljs-keyword">if</span> err := token.Error(); err != <span class="hljs-literal">nil</span> {
    slog.Error(<span class="hljs-string">"failed to connect"</span>, <span class="hljs-string">"error"</span>, err)
    <span class="hljs-keyword">return</span>
}
<span class="hljs-keyword">defer</span> client.Disconnect(disconnectTimeout)
slog.Info(<span class="hljs-string">"connected"</span>)
</code></pre>
<h2 id="heading-publishing-messages-to-azure-iot-hub">Publishing Messages to Azure IoT Hub</h2>
<p>Once connected, we can publish messages to Azure IoT Hub using the appropriate topic format. For device-to-cloud messaging, Azure IoT Hub expects messages on the topic <code>devices/{deviceId}/messages/events/</code>. The Publish method requires several parameters: the topic, Quality of Service (QoS) level, retain flag, and the message payload. For our example, we use QoS level 1, which ensures at-least-once delivery according to the MQTT specification (<a target="_blank" href="https://www.ibm.com/docs/en/integration-bus/10.1?topic=bus-quality-service-connection-management">IBM documentation on QoS</a><a target="_blank" href="https://www.ibm.com/docs/en/integration-bus/10.1?topic=bus-quality-service-connection-management">). The retain flag is se</a>t to true, indicating that the broker should keep the message for future subscribers. We use a simple "hello" string as our payload, which is converted to bytes before transmission. Similar to the connection process, we wait for the publish operation to complete or time out, and then check for any errors. A success message is logged when the message is successfully published to Azure IoT Hub.</p>
<pre><code class="lang-go"><span class="hljs-comment">// publish the message</span>
topic := <span class="hljs-string">"devices/"</span> + deviceID + <span class="hljs-string">"/messages/events/"</span>
payload := <span class="hljs-string">"hello"</span>
token = client.Publish(
    topic,           <span class="hljs-comment">// topic</span>
    <span class="hljs-number">1</span>,               <span class="hljs-comment">// qos</span>
    <span class="hljs-literal">true</span>,            <span class="hljs-comment">// retain</span>
    []<span class="hljs-keyword">byte</span>(payload), <span class="hljs-comment">//payload</span>
)
token.WaitTimeout(publishTimeout)
<span class="hljs-keyword">if</span> err := token.Error(); err != <span class="hljs-literal">nil</span> {
    slog.Error(<span class="hljs-string">"failed to publish"</span>, <span class="hljs-string">"error"</span>, err)
    <span class="hljs-keyword">return</span>
}
slog.Info(<span class="hljs-string">"message published"</span>)
</code></pre>
<h2 id="heading-complete-implementation">Complete Implementation</h2>
<p>Below is the complete Go code for connecting to Azure IoT Hub using MQTT with TLS security and SAS token authentication. This implementation includes all the components we've discussed: configuration, SAS token generation, client setup, connection establishment, and message publishing. Simply replace the placeholder values with your actual Azure IoT Hub information to get started.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"crypto/hmac"</span>
    <span class="hljs-string">"crypto/sha256"</span>
    <span class="hljs-string">"crypto/tls"</span>
    <span class="hljs-string">"crypto/x509"</span>
    <span class="hljs-string">"encoding/base64"</span>
    <span class="hljs-string">"log/slog"</span>
    <span class="hljs-string">"net/url"</span>
    <span class="hljs-string">"strconv"</span>
    <span class="hljs-string">"time"</span>

    mqtt <span class="hljs-string">"github.com/eclipse/paho.mqtt.golang"</span>
)

<span class="hljs-comment">// configuration</span>
<span class="hljs-keyword">const</span> (
    hubName         = <span class="hljs-string">"your-hub-name.azure-devices.net"</span>
    deviceID        = <span class="hljs-string">"your-device-id"</span>
    deviceKey       = <span class="hljs-string">"your-device-key"</span>
    tokenExpiration = <span class="hljs-number">60</span> * time.Minute
)

<span class="hljs-comment">// timeouts</span>
<span class="hljs-keyword">const</span> (
    connectTimeout    = <span class="hljs-number">5</span> * time.Second
    disconnectTimeout = <span class="hljs-number">3000</span> <span class="hljs-comment">// milliseconds</span>
    publishTimeout    = <span class="hljs-number">5</span> * time.Second
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// create the client options</span>
    brokerURL := <span class="hljs-string">"ssl://"</span> + hubName + <span class="hljs-string">":8883"</span>
    username := hubName + <span class="hljs-string">"/"</span> + deviceID
    sasToken := generateSASToken(hubName, deviceID, deviceKey, tokenExpiration)

    certPool, err := x509.SystemCertPool()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        slog.Error(<span class="hljs-string">"failed to load system certificate pool"</span>, <span class="hljs-string">"error"</span>, err)
        <span class="hljs-keyword">return</span>
    }

    tlsConfig := &amp;tls.Config{
        RootCAs:            certPool,
        MinVersion:         tls.VersionTLS12,
        InsecureSkipVerify: <span class="hljs-literal">false</span>,
        ServerName:         hubName,
    }

    opts := mqtt.NewClientOptions().
        AddBroker(brokerURL).
        SetClientID(deviceID).
        SetUsername(username).
        SetPassword(sasToken).
        SetTLSConfig(tlsConfig)

    <span class="hljs-comment">// create the client</span>
    client := mqtt.NewClient(opts)

    <span class="hljs-comment">// connect to the broker</span>
    token := client.Connect()
    token.WaitTimeout(connectTimeout)
    <span class="hljs-keyword">if</span> err := token.Error(); err != <span class="hljs-literal">nil</span> {
        slog.Error(<span class="hljs-string">"failed to connect"</span>, <span class="hljs-string">"error"</span>, err)
        <span class="hljs-keyword">return</span>
    }
    <span class="hljs-keyword">defer</span> client.Disconnect(disconnectTimeout)
    slog.Info(<span class="hljs-string">"connected"</span>)

    <span class="hljs-comment">// publish the message</span>
    topic := <span class="hljs-string">"devices/"</span> + deviceID + <span class="hljs-string">"/messages/events/"</span>
    payload := <span class="hljs-string">"hello"</span>
    token = client.Publish(
        topic,           <span class="hljs-comment">// topic</span>
        <span class="hljs-number">1</span>,               <span class="hljs-comment">// qos</span>
        <span class="hljs-literal">true</span>,            <span class="hljs-comment">// retain</span>
        []<span class="hljs-keyword">byte</span>(payload), <span class="hljs-comment">//payload</span>
    )
    token.WaitTimeout(publishTimeout)
    <span class="hljs-keyword">if</span> err := token.Error(); err != <span class="hljs-literal">nil</span> {
        slog.Error(<span class="hljs-string">"failed to publish"</span>, <span class="hljs-string">"error"</span>, err)
        <span class="hljs-keyword">return</span>
    }
    slog.Info(<span class="hljs-string">"message published"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">generateSASToken</span><span class="hljs-params">(hubName, deviceID, key <span class="hljs-keyword">string</span>, expiration time.Duration)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-comment">// create the timestamp for expiration</span>
    timestamp := time.Now().Add(expiration).Unix()
    timestampString := strconv.FormatInt(timestamp, <span class="hljs-number">10</span>)

    <span class="hljs-comment">// create the resource uri</span>
    uri := hubName + <span class="hljs-string">"/devices/"</span> + deviceID
    escapedURI := url.QueryEscape(uri)

    <span class="hljs-comment">// create the signature</span>
    signature := escapedURI + <span class="hljs-string">"\n"</span> + timestampString

    <span class="hljs-comment">// hash the signature</span>
    decodedKey, _ := base64.StdEncoding.DecodeString(key)
    hash := hmac.New(sha256.New, decodedKey)
    hash.Write([]<span class="hljs-keyword">byte</span>(signature))
    encryptedSignature := hash.Sum(<span class="hljs-literal">nil</span>)
    decodedSignature := base64.StdEncoding.EncodeToString(encryptedSignature)
    escapedSignature := url.QueryEscape(decodedSignature)

    <span class="hljs-comment">// create and return the token</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"SharedAccessSignature sr="</span> + escapedURI + <span class="hljs-string">"&amp;sig="</span> + escapedSignature + <span class="hljs-string">"&amp;se="</span> + timestampString
}
</code></pre>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>This implementation focuses on the essentials of connecting to Azure IoT Hub via MQTT. I personally couldn't find any straightforward Go examples for this specific scenario online, which made creating this guide a priority.</p>
<p>The code covers the basics of authentication and message publishing, though there's room for improvement. For production use, you'd want to add proper connection event handlers, implement automatic SAS token renewal, and further customize the TLS configuration.</p>
<p>I hope this example provides a useful starting point for your IoT projects with Go and Azure. While simplified, it contains the key elements needed to establish secure communication with your IoT Hub.</p>
]]></content:encoded></item><item><title><![CDATA[Go HTTP Server Timeouts]]></title><description><![CDATA[A few days ago, I was looking into an idle timeout error reported by a client using one of our APIs. To better understand HTTP server timeouts, I wrote some server and client code for testing, which I'd like to share with you. This might help others ...]]></description><link>https://ermanimer.hashnode.dev/go-http-server-timeouts</link><guid isPermaLink="true">https://ermanimer.hashnode.dev/go-http-server-timeouts</guid><category><![CDATA[Go Language]]></category><category><![CDATA[http server]]></category><category><![CDATA[timeout]]></category><category><![CDATA[read-timeout]]></category><category><![CDATA[write-timeout]]></category><category><![CDATA[idle-timeout]]></category><dc:creator><![CDATA[Erman İmer]]></dc:creator><pubDate>Fri, 23 Aug 2024 12:09:49 GMT</pubDate><content:encoded><![CDATA[<p>A few days ago, I was looking into an idle timeout error reported by a client using one of our APIs. To better understand HTTP server timeouts, I wrote some server and client code for testing, which I'd like to share with you. This might help others gain a clearer understanding. I believe the code is mostly self-explanatory, so I'll keep the explanations brief.</p>
<h3 id="heading-write-timeout">Write Timeout</h3>
<p><em>WriteTimeout is the maximum duration before timing out writes of the response.</em></p>
<p>To trigger a write timeout, we simulate a delay in the server's root handler. The client gets an <code>io.EOF</code> error when it tries to receive the response from the server.</p>
<h4 id="heading-servergo">server.go</h4>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// create a new HTTP request multiplexer (router)</span>
    h := http.NewServeMux()

    <span class="hljs-comment">// register the root handler</span>
    h.HandleFunc(<span class="hljs-string">"GET /"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        <span class="hljs-comment">// simulate a delay of 2 seconds to trigger a write timeout</span>
        time.Sleep(<span class="hljs-number">2</span> * time.Second)

        <span class="hljs-comment">// write the status</span>
        w.WriteHeader(http.StatusOK)
    })

    <span class="hljs-comment">// create the server with a write timeout of 1 second</span>
    s := &amp;http.Server{
        Addr:         <span class="hljs-string">":8080"</span>,
        Handler:      h,
        WriteTimeout: <span class="hljs-number">1</span> * time.Second,
    }

    <span class="hljs-comment">// start the server</span>
    fmt.Println(<span class="hljs-string">"server: listening..."</span>)
    err := s.ListenAndServe()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> &amp;&amp; err != http.ErrServerClosed {
        fmt.Printf(<span class="hljs-string">"server: %s\n"</span>, err.Error())
    }
}
</code></pre>
<h4 id="heading-clientgo">client.go</h4>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"os"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// try to send </span>
    resp, err := http.Get(<span class="hljs-string">"http://localhost:8080/"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"client: %s\n"</span>, err)
        os.Exit(<span class="hljs-number">1</span>)
    }
    <span class="hljs-keyword">defer</span> resp.Body.Close()
    fmt.Printf(<span class="hljs-string">"client: %s\n"</span>, resp.Status)
}
</code></pre>
<h4 id="heading-client-output">Client Output:</h4>
<pre><code class="lang-plaintext">client: Get "http://localhost:8080/": EOF
exit status 1
</code></pre>
<h3 id="heading-idle-timeout">Idle Timeout</h3>
<p><em>IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled.</em></p>
<p>To trigger an idle timeout, we simulate a delay in the client after sending the first request and reading the status of the first response from the server. The client gets an <code>io.EOF</code> error when it tries to read the status of the second response.</p>
<h4 id="heading-servergo-1">server.go</h4>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// create a new HTTP request multiplexer (router)</span>
    h := http.NewServeMux()

    <span class="hljs-comment">// register the root handler</span>
    h.HandleFunc(<span class="hljs-string">"GET /"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        <span class="hljs-comment">// write the status</span>
        w.WriteHeader(http.StatusOK)
    })

    <span class="hljs-comment">// create the server with an idle timeout of 1 second</span>
    s := &amp;http.Server{
        Addr:        <span class="hljs-string">":8080"</span>,
        Handler:     h,
        IdleTimeout: <span class="hljs-number">1</span> * time.Second,
    }

    <span class="hljs-comment">// start the server</span>
    fmt.Println(<span class="hljs-string">"server: listening..."</span>)
    err := s.ListenAndServe()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> &amp;&amp; err != http.ErrServerClosed {
        fmt.Printf(<span class="hljs-string">"server: %s\n"</span>, err.Error())
    }
}
</code></pre>
<h4 id="heading-clientgo-1">client.go</h4>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"bufio"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// connect to the server</span>
    conn, err := net.Dial(<span class="hljs-string">"tcp"</span>, <span class="hljs-string">"localhost:8080"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"client: %s\n"</span>, err.Error())
        os.Exit(<span class="hljs-number">1</span>)
    }

    <span class="hljs-comment">// write the first request</span>
    fmt.Fprintf(conn, <span class="hljs-string">"GET / HTTP/1.1\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"Host: localhost:8080\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"Connection: keep-alive\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"\r\n"</span>)

    <span class="hljs-comment">// read the status of the first response</span>
    reader := bufio.NewReader(conn)
    status, err := reader.ReadString(<span class="hljs-string">'\n'</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"client: %s\n"</span>, err.Error())
        os.Exit(<span class="hljs-number">1</span>)
    }
    fmt.Printf(<span class="hljs-string">"client: %s"</span>, status)

    <span class="hljs-comment">// discard the rest of the first response</span>
    <span class="hljs-keyword">for</span> {
        line, err := reader.ReadString(<span class="hljs-string">'\n'</span>)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            fmt.Println(<span class="hljs-string">"client: "</span>, err.Error())
            os.Exit(<span class="hljs-number">1</span>)
        }
        <span class="hljs-keyword">if</span> line == <span class="hljs-string">"\r\n"</span> {
            <span class="hljs-keyword">break</span>
        }
    }

    <span class="hljs-comment">// simulate a delay of 2 seconds to trigger an idle timeout</span>
    time.Sleep(<span class="hljs-number">2</span> * time.Second)

    <span class="hljs-comment">// write the second request</span>
    fmt.Fprintf(conn, <span class="hljs-string">"GET / HTTP/1.1\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"Host: localhost:8080\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"Connection: keep-alive\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"\r\n"</span>)

    <span class="hljs-comment">// try to read the status of the second response</span>
    status, err = reader.ReadString(<span class="hljs-string">'\n'</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"client: %s\n"</span>, err.Error())
        os.Exit(<span class="hljs-number">1</span>)
    }
    fmt.Printf(<span class="hljs-string">"client: %s"</span>, status)
}
</code></pre>
<h4 id="heading-client-output-1">Client Output</h4>
<pre><code class="lang-plaintext">client: HTTP/1.1 200 OK
client: EOF
exit status 1
</code></pre>
<h3 id="heading-read-timeout">Read Timeout</h3>
<p><em>ReadTimeout is the maximum duration for reading the entire request, including the body.</em></p>
<p>To trigger a read timeout, we simulate a delay in the client after it connects to the server. The client gets an <code>io.EOF</code> error when it tries to read the server's response status.</p>
<h4 id="heading-servergo-2">server.go</h4>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// create a new HTTP request multiplexer (router)</span>
    h := http.NewServeMux()

    <span class="hljs-comment">// register the root handler</span>
    h.HandleFunc(<span class="hljs-string">"GET /"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        <span class="hljs-comment">// write the status</span>
        w.WriteHeader(http.StatusOK)
    })

    <span class="hljs-comment">// create the server with a read timeout of 1 second</span>
    s := &amp;http.Server{
        Addr:        <span class="hljs-string">":8080"</span>,
        Handler:     h,
        ReadTimeout: <span class="hljs-number">1</span> * time.Second,
    }

    <span class="hljs-comment">// start the server</span>
    fmt.Println(<span class="hljs-string">"server: listening..."</span>)
    err := s.ListenAndServe()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> &amp;&amp; err != http.ErrServerClosed {
        fmt.Printf(<span class="hljs-string">"server: %s\n"</span>, err.Error())
    }
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"bufio"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// connect to the server</span>
    conn, err := net.Dial(<span class="hljs-string">"tcp"</span>, <span class="hljs-string">"localhost:8080"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"client: %s\n"</span>, err.Error())
        os.Exit(<span class="hljs-number">1</span>)
    }

    <span class="hljs-comment">// simulate a delay of 2 seconds to trigger a read timeout</span>
    time.Sleep(<span class="hljs-number">2</span> * time.Second)

    <span class="hljs-comment">// write the first request</span>
    fmt.Fprintf(conn, <span class="hljs-string">"GET / HTTP/1.1\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"Host: localhost:8080\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"Connection: keep-alive\r\n"</span>)
    fmt.Fprintf(conn, <span class="hljs-string">"\r\n"</span>)

    <span class="hljs-comment">// read the status of the first response</span>
    reader := bufio.NewReader(conn)
    status, err := reader.ReadString(<span class="hljs-string">'\n'</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"client: %s\n"</span>, err.Error())
        os.Exit(<span class="hljs-number">1</span>)
    }
    fmt.Printf(<span class="hljs-string">"client: %s"</span>, status)
}
</code></pre>
<h4 id="heading-client-output-2">Client Output</h4>
<pre><code class="lang-plaintext">client: EOF
exit status 1
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Improving the Performance of a Simple Key-Value Store in Go with Sharding]]></title><description><![CDATA[In this article, we will show you how to implement a simple key-value store in Go and then improve its performance by using sharding.
We can implement a simple key-value store that is concurrently safe by using a mutex. However, using a single mutex ...]]></description><link>https://ermanimer.hashnode.dev/improving-the-performance-of-a-simple-key-value-store-in-go-with-sharding</link><guid isPermaLink="true">https://ermanimer.hashnode.dev/improving-the-performance-of-a-simple-key-value-store-in-go-with-sharding</guid><category><![CDATA[golang]]></category><category><![CDATA[sharding]]></category><category><![CDATA[in-memory-key-value-store]]></category><dc:creator><![CDATA[Erman İmer]]></dc:creator><pubDate>Thu, 07 Sep 2023 14:59:01 GMT</pubDate><content:encoded><![CDATA[<p>In this article, we will show you how to implement a simple key-value store in Go and then improve its performance by using sharding.</p>
<p>We can implement a simple key-value store that is concurrently safe by using a mutex. However, using a single mutex creates a bottleneck, as all concurrent requests will have to wait for the mutex to be released before they can proceed.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> store

<span class="hljs-keyword">import</span> <span class="hljs-string">"sync"</span>

<span class="hljs-keyword">type</span> Store <span class="hljs-keyword">interface</span> {
    Get(key <span class="hljs-keyword">string</span>) ([]<span class="hljs-keyword">byte</span>, <span class="hljs-keyword">bool</span>)
    Set(key <span class="hljs-keyword">string</span>, value []<span class="hljs-keyword">byte</span>)
}

<span class="hljs-keyword">type</span> store <span class="hljs-keyword">struct</span> {
    mu    *sync.RWMutex
    items <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>][]<span class="hljs-keyword">byte</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewStore</span><span class="hljs-params">()</span> <span class="hljs-title">Store</span></span> {
    <span class="hljs-keyword">return</span> &amp;store{
        mu:    &amp;sync.RWMutex{},
        items: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>][]<span class="hljs-keyword">byte</span>),
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *store)</span> <span class="hljs-title">Get</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">byte</span>, <span class="hljs-keyword">bool</span>)</span></span> {
    s.mu.RLock()
    <span class="hljs-keyword">defer</span> s.mu.RUnlock()
    value, ok := s.items[key]
    <span class="hljs-keyword">return</span> value, ok
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *store)</span> <span class="hljs-title">Set</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>, value []<span class="hljs-keyword">byte</span>)</span></span> {
    s.mu.Lock()
    <span class="hljs-keyword">defer</span> s.mu.Unlock()
    s.items[key] = value
}
</code></pre>
<p>We can write an integration test to set and get one million records concurrently.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> store

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"reflect"</span>
    <span class="hljs-string">"testing"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestIntegration</span><span class="hljs-params">(t *testing.T)</span></span> {
    s := NewStore()
    goroutines := <span class="hljs-number">1024</span>
    count := <span class="hljs-number">1</span>_000_000
    sem := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, goroutines)

    <span class="hljs-comment">// concurrent sets</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; count; i++ {
        sem &lt;- <span class="hljs-keyword">struct</span>{}{}
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(j <span class="hljs-keyword">int</span>)</span></span> {
            <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
                &lt;-sem
            }()
            key, value := makeKeyValuePair(j)
            s.Set(key, value)
        }(i)
    }
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; goroutines; i++ {
        sem &lt;- <span class="hljs-keyword">struct</span>{}{}
    }

    <span class="hljs-comment">// concurrent gets</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; count; i++ {
        &lt;-sem
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(j <span class="hljs-keyword">int</span>)</span></span> {
            <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
                sem &lt;- <span class="hljs-keyword">struct</span>{}{}
            }()
            key, expected := makeKeyValuePair(j)
            actual, ok := s.Get(key)
            <span class="hljs-keyword">if</span> !ok {
                t.Errorf(<span class="hljs-string">"key doesn't exist: '%s'"</span>, key)
            }
            <span class="hljs-keyword">if</span> !reflect.DeepEqual(expected, actual) {
                t.Errorf(<span class="hljs-string">"expected doesn't match actual, expected: '%s' actual: '%s'"</span>, expected, actual)
            }
        }(i)
    }
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; goroutines; i++ {
        &lt;-sem
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeKeyValuePair</span><span class="hljs-params">(i <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">string</span>, []<span class="hljs-keyword">byte</span>)</span></span> {
    key := fmt.Sprintf(<span class="hljs-string">"%d"</span>, i)
    value := []<span class="hljs-keyword">byte</span>(fmt.Sprintf(<span class="hljs-string">"%d"</span>, i))
    <span class="hljs-keyword">return</span> key, value
}
</code></pre>
<p>This test is completed in 2.23 seconds on my machine.</p>
<p>To solve the bottleneck caused by the single mutex, we can divide the data into shards, each with its own mutex. This reduces contention for the mutex, which can improve throughput and latency.</p>
<p>A shard is essentially a smaller key-value store that can be accessed independently.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> store

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"crypto/md5"</span>
    <span class="hljs-string">"encoding/hex"</span>
    <span class="hljs-string">"sync"</span>
)

<span class="hljs-keyword">const</span> defaultShardKeyLength = <span class="hljs-number">2</span> <span class="hljs-comment">// default shard count: 16^2</span>

<span class="hljs-keyword">type</span> shard <span class="hljs-keyword">struct</span> {
    mu    *sync.RWMutex
    items <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>][]<span class="hljs-keyword">byte</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeShardKey</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">string</span></span> {
    h := md5.New()
    h.Write([]<span class="hljs-keyword">byte</span>(key))
    sum := h.Sum(<span class="hljs-literal">nil</span>)
    encoded := hex.EncodeToString(sum)
    <span class="hljs-keyword">return</span> encoded[:defaultShardKeyLength]
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">newShard</span><span class="hljs-params">()</span> *<span class="hljs-title">shard</span></span> {
    <span class="hljs-keyword">return</span> &amp;shard{
        mu:    &amp;sync.RWMutex{},
        items: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>][]<span class="hljs-keyword">byte</span>),
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *shard)</span> <span class="hljs-title">get</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">byte</span>, <span class="hljs-keyword">bool</span>)</span></span> {
    s.mu.RLock()
    <span class="hljs-keyword">defer</span> s.mu.RUnlock()
    v, ok := s.items[key]
    <span class="hljs-keyword">return</span> v, ok
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *shard)</span> <span class="hljs-title">set</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>, value []<span class="hljs-keyword">byte</span>)</span></span> {
    s.mu.Lock()
    <span class="hljs-keyword">defer</span> s.mu.Unlock()
    s.items[key] = value
}
</code></pre>
<p>We can modify our code to implement sharding.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> store

<span class="hljs-keyword">import</span> <span class="hljs-string">"sync"</span>

<span class="hljs-keyword">type</span> Store <span class="hljs-keyword">interface</span> {
    Get(key <span class="hljs-keyword">string</span>) ([]<span class="hljs-keyword">byte</span>, <span class="hljs-keyword">bool</span>)
    Set(key <span class="hljs-keyword">string</span>, value []<span class="hljs-keyword">byte</span>)
}

<span class="hljs-keyword">type</span> store <span class="hljs-keyword">struct</span> {
    mu     *sync.RWMutex
    shards <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*shard
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewStore</span><span class="hljs-params">()</span> <span class="hljs-title">Store</span></span> {
    <span class="hljs-keyword">return</span> &amp;store{
        mu:     &amp;sync.RWMutex{},
        shards: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*shard),
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *store)</span> <span class="hljs-title">Get</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">byte</span>, <span class="hljs-keyword">bool</span>)</span></span> {
    shardKey := makeShardKey(key)
    s.mu.RLock()
    shard, ok := s.shards[shardKey]
    <span class="hljs-keyword">if</span> !ok {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, <span class="hljs-literal">false</span>
    }
    s.mu.RUnlock()

    <span class="hljs-keyword">return</span> shard.get(key)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *store)</span> <span class="hljs-title">Set</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>, value []<span class="hljs-keyword">byte</span>)</span></span> {
    shardKey := makeShardKey(key)
    s.mu.Lock()
    shard, ok := s.shards[shardKey]
    <span class="hljs-keyword">if</span> !ok {
        shard = newShard()
        s.shards[shardKey] = shard
    }
    s.mu.Unlock()

    shard.set(key, value)
}
</code></pre>
<p>If we run the integration test, it completes in 1.78 seconds, which is roughly 20% faster than the simple key-value store. This suggests that sharding has improved performance under the conditions of this test.</p>
<p>In conclusion, sharding can be a useful technique for improving the performance of a key-value store.</p>
<p>Thank you for reading my article. I hope that you found it helpful. If you have any questions or feedback, please feel free to leave a comment below.</p>
]]></content:encoded></item><item><title><![CDATA[Tail Call Optimization In Elixir]]></title><description><![CDATA[A tail-recursive function executes itself as the last thing.
def fun(...) do
    ...
    fun(...)
end

Elixir optimizes tail calls by reusing the last stack frame of the function, thus avoiding the typical stack push and reducing the overhead of crea...]]></description><link>https://ermanimer.hashnode.dev/tail-call-optimization-in-elixir</link><guid isPermaLink="true">https://ermanimer.hashnode.dev/tail-call-optimization-in-elixir</guid><category><![CDATA[Elixir]]></category><category><![CDATA[tail call optimization]]></category><category><![CDATA[tail recursion]]></category><dc:creator><![CDATA[Erman İmer]]></dc:creator><pubDate>Thu, 27 Jul 2023 09:56:01 GMT</pubDate><content:encoded><![CDATA[<p>A <a target="_blank" href="https://en.wikipedia.org/wiki/Tail_call">tail-recursive</a> function executes itself as the last thing.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fun</span></span>(...) <span class="hljs-keyword">do</span>
    ...
    fun(...)
<span class="hljs-keyword">end</span>
</code></pre>
<p>Elixir optimizes tail calls by reusing the last stack frame of the function, thus avoiding the typical stack push and reducing the overhead of creating and removing stack frames during function calls.</p>
<p>This function calculates the nth Fibonacci sequence element.</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">Fibonacci</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">nth</span></span>(n) <span class="hljs-keyword">when</span> n &lt;= <span class="hljs-number">1</span>, <span class="hljs-symbol">do:</span> n

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">nth</span></span>(n) <span class="hljs-keyword">do</span>
    nth(n - <span class="hljs-number">1</span>) + nth(n - <span class="hljs-number">2</span>)
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>This function is not tail-recursive, as the tail call involves the summation of two functions.</p>
<p>This is (my) tail recursive version of the same function:</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">Fibonacci</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">nth</span></span>(n) <span class="hljs-keyword">when</span> n &lt;= <span class="hljs-number">1</span>, <span class="hljs-symbol">do:</span> n

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">nth</span></span>(n) <span class="hljs-keyword">do</span>
    fib(n - <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, 0)
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">fib</span></span>(0, minus_1, minus_2) <span class="hljs-keyword">do</span>
    minus_1 + minus_2
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">fib</span></span>(n, minus_1, minus_2) <span class="hljs-keyword">do</span>
    fib(n - <span class="hljs-number">1</span>, minus_1 + minus_2, minus_1)
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>This function is tail-recursive, as the tail call directly invokes the function itself.</p>
<p>Let's compare the execution duration of the two functions with the following code one by one.</p>
<pre><code class="lang-elixir">start_time = <span class="hljs-symbol">:os</span>.system_time(<span class="hljs-symbol">:native</span>)
result = Fibonacci.nth(<span class="hljs-number">20</span>)
end_time = <span class="hljs-symbol">:os</span>.system_time(<span class="hljs-symbol">:native</span>)
duration = end_time - start_time
IO.puts(<span class="hljs-string">"result: <span class="hljs-subst">#{result}</span> duration: <span class="hljs-subst">#{duration}</span> nanoseconds"</span>)
</code></pre>
<p>Output of the first version:</p>
<pre><code class="lang-bash">result: 6765 duration: 60000 nanoseconds
</code></pre>
<p>Output of the optimized version:</p>
<pre><code class="lang-bash">result: 6765 duration: 1000 nanoseconds
</code></pre>
<p>The tail-call optimization significantly improved the execution time.</p>
<p>Thanks for reading!</p>
]]></content:encoded></item></channel></rss>