<?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" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Greycell's Newsletter]]></title><description><![CDATA[Software development, productivity and everything to help you improve in bite-size]]></description><link>https://blog.greycell.dev</link><image><url>https://substackcdn.com/image/fetch/$s_!uahU!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c4cb8-06e9-4827-96bb-58848b533203_775x775.png</url><title>Greycell&apos;s Newsletter</title><link>https://blog.greycell.dev</link></image><generator>Substack</generator><lastBuildDate>Thu, 09 Apr 2026 03:06:05 GMT</lastBuildDate><atom:link href="https://blog.greycell.dev/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Bharghava Varun Ayada]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[greycell@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[greycell@substack.com]]></itunes:email><itunes:name><![CDATA[Bharghava Varun Ayada]]></itunes:name></itunes:owner><itunes:author><![CDATA[Bharghava Varun Ayada]]></itunes:author><googleplay:owner><![CDATA[greycell@substack.com]]></googleplay:owner><googleplay:email><![CDATA[greycell@substack.com]]></googleplay:email><googleplay:author><![CDATA[Bharghava Varun Ayada]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Strings in Go]]></title><description><![CDATA[Understanding strings and string conversion in Go programming language]]></description><link>https://blog.greycell.dev/p/strings-in-go</link><guid isPermaLink="false">https://blog.greycell.dev/p/strings-in-go</guid><dc:creator><![CDATA[Bharghava Varun Ayada]]></dc:creator><pubDate>Mon, 28 Dec 2020 09:16:41 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/0fa501c6-ad0d-4c2b-ab7d-552be1cd21f3_3335x2500.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>String Types</h2><p>In Go, a string value is a sequence of bytes. A string value could be empty and the number of bytes is called the length of the string and is never negative. Strings are immutable: once created, it is impossible to change the contents of a string.<br>The predeclared string type is <code>string</code>; it is a defined type.<br></p><p>Normally, the length of a string <code>s</code> can be discovered using the built-in function <code>len</code>. This function returns the number of bytes in a string, not the number of characters. This distinction is important because strings in Go support unicode encoding. So a character in a UTF-8 string can be more than one byte. For instance, this is a valid string in Go. Some of the characters here are not even valid UTF-8.</p><pre><code>const sample = "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98"</code></pre><p><br>Printing this string to stdout will produce the following output.</p><pre><code>&#65533;&#65533;=&#65533; &#8984;</code></pre><h2><br><strong>Length of a string</strong></h2><p>Like we discussed in the previous section, the built-in function <code>len</code> returns the number of bytes in a string. So, for instance, if our sample string is &#8220;Well done &#128077;&#127996;&#8221;, then <code>len</code> will return <strong>18</strong>.</p><pre><code>s := "Well done &#128077;&#127996;"
fmt.Printf("len(s) = %d\n", len(s))</code></pre><p><br>The output is:</p><pre><code>len(s) = 18</code></pre><p><br>Now, instead of number of bytes, if we wanted to count the number of characters in the string, then we would need to use a different function. May be the standard <code>RuneCountInString</code> function from <code>utf8</code> might come in handy. Let's try that here:</p><pre><code>s := "Well done &#128077;&#127996;"

fmt.Printf("# of characters in s = %d\n", utf8.RuneCountInString(s))</code></pre><p><br>The output is:</p><pre><code># of characters in s = 12</code></pre><p><br>This function here returned <code>12</code> as the output, but we have only <code>11</code> characters in the string <code>s</code>. This is because the function <code>RuneCountInString</code> returns the number of runes in the string. Each unicode code point in a string is represented as a rune literal. </p><p>Let's see what the rune literals are here:</p><pre><code>s := "Well done &#128077;&#127996;"
for _, i := range []rune(s) {
    fmt.Printf("%v ", i)
}</code></pre><p><br>The output is:</p><pre><code>87 101 108 108 32 100 111 110 101 32 128077 127996</code></pre><p><br>As expected, we have 12 rune literals in the string <code>s.</code> Note that each english alphabet is a single rune literal, which is equivalent to it&#8217;s respective ASCII value, but the emoji character requires two rune literals <code>128077</code> and <code>127996</code>. This is because the emoji used here requires a separate rune or byte to represent the skin tone. So then, how do we actually figure out the number of characters?</p><p><br>There is an open source library <code>github.com/rivo/uniseg</code> that provides a function to do just this. It provides a function <code>GraphemeClusterCount</code> that returns the number of characters that is present in this string. Check out the source code on <a href="https://github.com/rivo/uniseg">Github</a> for details about the author and the implementation.</p><p><br>Now let's try this library and see if we can get the actual number of characters in the given string.</p><pre><code>import "github.com/rivo/uniseg"

s := "Well done &#128077;&#127996;"

fmt.Printf("uniseg.GraphemeClusterCount(s) = %d\n", uniseg.GraphemeClusterCount(s3))</code></pre><p></p><p>The output is:</p><pre><code>uniseg.GraphemeClusterCount(s3) = 11</code></pre><p></p><p>Finally, we have a function that provides the right value for the number of the characters in the given string. This works in almost all cases that I have tested. Please do run your own tests before using this.</p><h2><strong>String Conversion</strong></h2><p>Before we end this article, let's look at a common mistake that beginners to Go might encounter when working with strings. Let's look at a snippet here.</p><pre><code>s1 := string(65)
fmt.Printf("length of s1 = %d\n", len(s1))

s2 := strconv.FormatInt(65, 10)
fmt.Printf("length of s2 = %d\n", len(s2))```</code></pre><p></p><p>Here the output would look like this:</p><pre><code>length of s1 = 1
length of s2 = 2</code></pre><p><br>This is because the <code>string(65)</code> returns the character that is represented by integer 65 in <a href="https://unicodelookup.com/#65/1">unicode</a>. In this case, that value is character <strong>A</strong>. So the length of <code>s1</code> is 1. To actually convert the integer to string, use the function <code>FormatInt</code> from <code>strconv</code> package. Here <code>strconv.FormatInt(65, 10)</code> converts base 10 representation of <strong>65</strong> to a string. Hence the variable <code>s2</code> is set to <code>"65"</code> and the length of <code>s2</code> is 2.<br></p><h2><strong>References</strong></h2><ul><li><p>Strings, bytes, runes and characters in Go <a href="https://blog.golang.org/strings">https://blog.golang.org/strings</a></p></li><li><p>String types <a href="https://golang.org/ref/spec#String_types">https://golang.org/ref/spec#String_types</a></p></li><li><p>rivo/uniseg package <a href="https://github.com/rivo/uniseg">https://github.com/rivo/uniseg</a></p></li><li><p>strconv package <a href="https://pkg.go.dev/strconv">https://pkg.go.dev/strconv</a></p></li></ul><p>I hope this was helpful and if you find any errors or issues in this post, please do let me know in the comments section below.</p><p>In case you would like to get notified about more articles like this, please subscribe.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.greycell.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.greycell.dev/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Vanity URL for Go packages]]></title><description><![CDATA[A walk through on how to setup a vanity URL for your organization's Go packages.]]></description><link>https://blog.greycell.dev/p/vanity-url-for-go-packages</link><guid isPermaLink="false">https://blog.greycell.dev/p/vanity-url-for-go-packages</guid><dc:creator><![CDATA[Bharghava Varun Ayada]]></dc:creator><pubDate>Wed, 02 Dec 2020 20:06:58 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/21d4c70a-e416-4446-b50f-794bae36d663_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you have been working with Go programming language for a while, you would have noticed that a lot of open source packages that you import start with "<code>github.com/&#8230;"</code>. You would then use <code>go get </code>command to download the package and add it to your <code>go.mod</code> file. For instance:</p><pre><code>$ go get -u github.com/abvarun226/goiplookup</code></pre><p>What if you did not want this dependency on Github and rather wanted to host your own git server? Or may be you want to move your code to Gitlab or Bitbucket but you are worried about your users having to fix their import paths. These problems can be solved with a vanity URL.</p><p>With vanity URL, the idea is to use a custom domain (for example: <code>gopkg.in/yaml.v3</code>) to import packages rather than using <code>github.com</code> in the package name. Your code can still be hosted on Github, but you&#8217;d need a service that this vanity URL resolves to, that will provide a way for <code>go get</code> command to checkout the source code from.  </p><h2>How does go get work?</h2><p>For <code>gopkg.in/yaml.v3,</code> the <code>go get</code> command basically makes a HTTP GET request to <code>https://gopkg.in/yaml.v3?go-get=1</code> URL. The response is an HTML content with certain &lt;meta&gt; tags that indicate to go get command about where it can clone the source code from. Here is an example of the response:</p><pre><code>$ http --body "https://gopkg.in/yaml.v3?go-get=1"
&lt;html&gt;
&lt;head&gt;
&lt;meta name="go-import" content="gopkg.in/yaml.v3 git https://gopkg.in/yaml.v3"&gt;
&lt;meta name="go-source" content="gopkg.in/yaml.v3 _ https://github.com/go-yaml/yaml/tree/v3{/dir} https://github.com/go-yaml/yaml/blob/v3{/dir}/{file}#L{line}"&gt;
&lt;/head&gt;
&lt;body&gt;
go get gopkg.in/yaml.v3
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>The content in <code>go-import</code> meta tag indicates where and how to download the source code. For instance, you can clone the yaml.v3 repository using git.</p><pre><code>$ git clone https://gopkg.in/yaml.v3
Cloning into 'yaml.v3'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 1643 (delta 0), reused 2 (delta 0), pack-reused 1637
Receiving objects: 100% (1643/1643), 1.52 MiB | 1.37 MiB/s, done.
Resolving deltas: 100% (1069/1069), done.</code></pre><h2>Server Implementation details</h2><p>Based on the details of how <code>go get</code> command works, all we need to do is return HTML page with appropriate <code>&lt;meta&gt;</code> tags. For our implementation, let&#8217;s say we want the vanity URL <code>ayada.dev</code> and the package we are interested in is <code>yaml</code>. So we want to be able to import the package using <code>ayada.dev/pkg/yaml</code>. Now, we must host the source code on Github, let&#8217;s say at <code>github.com/abvarun226/yaml</code>. So the HTTP response from <code>https://ayada.dev/pkg/yaml?go-get=1</code> should look like this:</p><pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt;
&lt;meta name="go-import" content="ayada.dev/pkg/yaml git ssh://git@github.com/abvarun226/yaml"&gt;
&lt;/head&gt;
&lt;/html&gt;</code></pre><p>If you have very few packages, you can simply use a static file server to serve this html content. If you have more than a few packages or multiple organizations that you would want to have a vanity URL for, then it makes sense to setup a web server that can handle this for you.</p><p>We can have a config file describe the import path and the corresponding repository path in the version control system (vcs). The server will use this config file to render the HTML content with appropriate <code>go-import</code> meta tag. It will also automatically handle sub-packages for you.</p><p>Let&#8217;s say we have two different users in Github with different Go packages. Below is one such config file (P.S. these repo path don&#8217;t exist, just an example).</p><pre><code>[
    {
        "importroot": "ayada.dev/pkg",
        "vcs": "git",
        "reporoot": "https://github.com/abvarun226"
    },
    {
        "importroot": "ayada.dev/paas",
        "vcs": "git",
        "reporoot": "https://github.com/ayadapaas"
    }
]</code></pre><p>For example, package with import path <code>ayada.dev/pkg/mypkg</code> will be redirected to <code>https://github.com/abvarun226/mypkg</code> repository path.</p><p>And package with import path <code>ayada.dev/paas/mypkg</code> will be redirected to <code>https://github.com/ayadapaas/mypkg</code> repository path.</p><p>The server handler code that does this redirect looks like this:</p><pre><code>package handler

import (
    "bytes"
    "net/http"
    "strings"
    "text/template"
)

// VanityServer redirects browsers to godoc or go tool to VCS repository.
func (h *Handler) VanityServer(w http.ResponseWriter, r *http.Request) {
    // Only allow GET method
    if r.Method != http.MethodGet {
        status := http.StatusMethodNotAllowed
        http.Error(w, http.StatusText(status), status)
        return
    }

    pkgName := r.Host + r.URL.Path

    // If go-get param is absent, redirect to godoc URL.
    if r.FormValue("go-get") != "1" {
        if h.opts.GodocURL == "" {
            w.Write([]byte(nothingHere))
            return
        }
        url := h.opts.GodocURL + pkgName
        http.Redirect(w, r, url, http.StatusTemporaryRedirect)
        return
    }

    // go-import mapping rules.
    var importRoot, vcs, repoRoot string
    for _, rule := range h.opts.MappingRules {
        if strings.HasPrefix(strings.ToLower(pkgName), strings.ToLower(rule.ImportRoot)+"/") {
            repoName := strings.Replace(strings.ToLower(pkgName), strings.ToLower(rule.ImportRoot), "", -1)
            repoName = strings.Split(repoName, "/")[1]

            importRoot = rule.ImportRoot + "/" + repoName
            repoRoot = rule.RepoRoot + "/" + repoName
            vcs = rule.VCS

            break
        }
    }

    // Create HTML template with go-import &lt;meta&gt; tag
    d := struct {
        ImportRoot string
        VCS        string
        RepoRoot   string
    }{
        ImportRoot: importRoot,
        VCS:        vcs,
        RepoRoot:   repoRoot,
    }

    var buf bytes.Buffer
    err := tmpl.Execute(&amp;buf, &amp;d)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Cache-Control", "public, max-age=900")
    w.Write(buf.Bytes())
}

var tmpl = template.Must(template.New("main").Parse(`&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt;
&lt;meta name="go-import" content="{{.ImportRoot}} {{.VCS}} {{.RepoRoot}}"&gt;
&lt;/head&gt;
&lt;/html&gt;
`))

const nothingHere = `
&lt;html&gt;
&lt;head&gt;&lt;/head&gt;
&lt;body&gt;&lt;h5&gt;Nothing here. Move along.&lt;/h5&gt;&lt;/body&gt;
&lt;/html&gt;
`
</code></pre><p></p><p>The response from this server looks like this:</p><pre><code>$ http --body "http://ayada.dev/paas/yaml/util?go-get=1"
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt;
&lt;meta name="go-import" content="ayada.dev/paas/yaml git https://github.com/ayadapaas/yaml"&gt;
&lt;/head&gt;
&lt;/html&gt;</code></pre><p>Without the <code>go-get=1</code> GET parameter, the server will either redirect to a Godoc URL or display &#8220;<em>Nothing here. Move along</em>&#8221; message.</p><p>With this response, go get command should be able to download the source from the given VCS path.</p><h2>Final thoughts</h2><p>In organizations that host their own git server, it makes sense to have a vanity URL for the Go packages. Open source developers can host their Go packages on any VCS. It is pretty easy to setup a server that handles vanity URLs for Go packages, and let the server direct <code>go get</code> command about which VCS to download the source code from. This makes moving Go packages between different VCS straightforward without worrying about changing the import paths in dependent packages.</p><p>If you are interested, the complete code for the vanity server is available here: https://github.com/abvarun226/vanity-server</p><p>I hope this was helpful and if you find any errors or issues in this post, please do let me know in the comments section below.</p><p>In case you would like to get notified about more articles like this, please subscribe.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.greycell.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.greycell.dev/subscribe?"><span>Subscribe now</span></a></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Microservices and cloud native development using Micro]]></title><description><![CDATA[An introduction to micro, a cloud native development platform written in Go]]></description><link>https://blog.greycell.dev/p/microservices-and-cloud-native-development</link><guid isPermaLink="false">https://blog.greycell.dev/p/microservices-and-cloud-native-development</guid><dc:creator><![CDATA[Bharghava Varun Ayada]]></dc:creator><pubDate>Mon, 30 Nov 2020 19:31:34 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/1931a1f4-2259-432f-836f-3124fa5f830c_1024x512.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For the past few years, Microservices has been all the rage and it has helped a lot of organizations to adopt continuous delivery and evolve its technology stack. There are quite a few programming tool kits and platforms that have sprung up to enable building Microservices. One among them is <a href="https://micro.mu/">Micro</a>, which is a cloud native development platform that addresses the key requirements for building services in the cloud. In their latest version v3, Micro provides service discovery, pub/sub, transport protocols and many other features out of the box. It is pretty easy to define protocol buffers and grpc services using Micro v3. Here I will provide a walk through of how to setup a simple micro service using Micro v3.</p><h2>Micro gateway server</h2><p>For this walk through, we want to be able to make HTTP calls to the service and the service is expected to provide JSON data. We&#8217;ll need a micro server that acts as the gateway and all services will register themselves with this server. The micro gateway can be made to authenticate or ratelimit requests by defining plugins, but let&#8217;s save this for a future post. For now, all we want is run a micro server that acts as the gateway and a service that actually has the business logic.</p><p>Now in order to run our micro gateway, we need to build the micro binary and run it. All of this is done on my local computer, without any docker containers, to make it simple.</p><pre><code>$ git clone https://github.com/micro/micro.git

$ cd micro

$ git checkout v3.0.2

# Ensure Go is installed on your computer.
$ go version
go version go1.14.9 darwin/amd64

# Let's build the micro server binary.
$ go build -o micro .

# Note the location of this binary. We'll need it later.
$ cp ./micro /usr/local/bin/micro</code></pre><p></p><p>Now that the micro server binary is ready, let&#8217;s start the micro gateway server.</p><pre><code>
$ ./micro server
2020-11-30 22:54:20  file=user/user.go:45 level=info Loading config key from /Users/greycell/.micro/config_secret_key
2020-11-30 22:54:20  file=user/user.go:80 level=info Loading keys /Users/greycell/.micro/id_rsa and /Users/greycell/.micro/id_rsa.pub
2020-11-30 22:54:20  file=server/server.go:86 level=info Starting server
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering network
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering runtime
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering registry
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering config
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering store
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering broker
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering events
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering auth
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering proxy
2020-11-30 22:54:20  file=server/server.go:111 level=info Registering api
2020-11-30 22:54:20  file=server/server.go:198 level=info Starting server runtime
2020-11-30 22:54:20  file=user/user.go:80 level=info Loading keys /Users/greycell/.micro/id_rsa and /Users/greycell/.micro/id_rsa.pub
2020-11-30 22:54:20  file=service/service.go:190 level=info Starting [service] server
2020-11-30 22:54:20  file=grpc/grpc.go:923 level=info Server [grpc] Listening on [::]:10001
2020-11-30 22:54:20  file=grpc/grpc.go:753 level=info Registry [mdns] Registering node: server-f0fd58e3-a6cc-4b3c-9fe3-4ae4506ce358</code></pre><p>Now that our micro gateway has started successfully, let&#8217;s create a new micro service, may be a comment service.</p><h2>Comment microservice</h2><p>The micro gateway communicates with the comment service using grpc. It will use protocol buffers as a medium to communicate with the service. So, let&#8217;s first define our protocol buffers for comment service.</p><pre><code>syntax = "proto3";

package comment;

service Comment {
    rpc New(NewRequest) returns (NewResponse) {}
    rpc List(ListRequest) returns (ListResponse) {}
}

message Item {
    string id = 1;
    string post = 2;
    string author = 3;
    string message = 4;
    int64 created = 5;
}

message NewRequest {
    string post = 1;
    string author = 2;
    string message = 3;
}

message NewResponse {
    Item comment = 1;
}

message ListRequest {
    string post = 1;
}

message ListResponse {
    repeated Item comments = 1;
}
</code></pre><p>Our comment service supports only two endpoints, one for creating a new comment and another to list comments in a post.</p><p>We also create a protocol buffer message called <em>Item</em> which basically defines the structure of a single comment. The rpc handler <em>New</em> is for creating a new comment. It accepts <em>NewRequest</em> as the input. The <em>NewRequest</em> message contains the information needed to create a new comment. It returns <em>NewResponse</em> which basically contains the comment<em> </em>data defined in <em>Item</em>.</p><p>The rpc handler <em>List</em> is for listing all the comments in a post. Naturally it&#8217;ll require the post id which is provided by the message <em>ListRequest</em>. The response is an array of comment <em>Item </em>which is provided by <em>ListResponse</em> message. Let&#8217;s save this file under a directory called <em>proto</em>. Our directory structure for the comment service will look like this.</p><pre><code>.
&#9500;&#9472;&#9472; Makefile
&#9500;&#9472;&#9472; README.md
&#9500;&#9472;&#9472; go.mod
&#9500;&#9472;&#9472; go.sum
&#9500;&#9472;&#9472; handler
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; comment.go
&#9500;&#9472;&#9472; main.go
&#9492;&#9472;&#9472; proto
    &#9500;&#9472;&#9472; comment.pb.go
    &#9500;&#9472;&#9472; comment.pb.micro.go
    &#9492;&#9472;&#9472; comment.proto</code></pre><p>In order to generate the protocol buffers, we&#8217;ll need a couple of packages. Let&#8217;s compile the list of all the commands we need into a Makefile.</p><pre><code>
GOPATH:=$(shell go env GOPATH)
MODIFY=Mgithub.com/micro/micro/proto/api/api.proto=github.com/micro/micro/v3/proto/api

.PHONY: init
init:
&#9;go get -u github.com/golang/protobuf/proto
&#9;go get -u github.com/golang/protobuf/protoc-gen-go
&#9;go get github.com/micro/micro/v3/cmd/protoc-gen-micro

.PHONY: proto
proto:    
&#9;protoc --proto_path=. --micro_out=${MODIFY}:. --go_out=${MODIFY}:. proto/comment.proto

.PHONY: build
build: proto
&#9;go build -o service *.go

.PHONY: test
test:
&#9;go test -v ./... -cover
</code></pre><p>Let&#8217;s run the <em>init</em> target to install the packages we need and then run the <em>proto</em> target to generate the micro service interfaces.</p><pre><code>$ make init proto</code></pre><p>The <em>proto</em> target will create two files under <em>proto</em> directory called <em>comment.pb.micro.go</em> and <em>comment.pb.go</em>. These two files will contain the Go structs for the protocol buffer messages we defined above and also the service handlers required to create and list comments.</p><p>Now, let&#8217;s define the business logic for our service handlers, <em>New</em> and <em>List</em>, in the file <em>comment.go</em> under <em>handler</em> directory.</p><pre><code>// file: ./handler/comment.go

package handler

import (
    "context"
    "time"

    pb "github.com/abvarun226/comment/proto"
    "github.com/google/uuid"
)

// Comment struct.
type Comment struct{}

// NewComment creates a new comment handler.
func NewComment() *Comment {
    return &amp;Comment{}
}

// New handler to create a new comment.
func (c *Comment) New(ctx context.Context, req *pb.NewRequest, rsp *pb.NewResponse) error {
    rsp.Comment = &amp;pb.Item{
        Id:      uuid.New().String(),
        Post:    req.Post,
        Author:  req.Author,
        Message: req.Message,
        Created: time.Now().UTC().Unix(),
    }
    return nil
}

// List handler to list comments in a post.
func (c *Comment) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
    rsp.Comments = []*pb.Item{
        {
            Id:      uuid.New().String(),
            Post:    req.Post,
            Author:  "greycell",
            Message: "this is a comment",
            Created: 1604650806,
        },
    }
    return nil
}
</code></pre><p></p><p>Now that we have our handlers to handle the business logic, let&#8217;s create the service and register it with the micro gateway server so that the gateway knows how to route requests to comment service.</p><pre><code>// file: ./main.go

package main

import (
    "github.com/abvarun226/comment/handler"
    "github.com/micro/micro/v3/service"
    "github.com/micro/micro/v3/service/logger"
)

const (
    // ServiceName is the name of this service.
    ServiceName = "comment"

    // ServerAddress is the registry address.
    ServerAddress = "127.0.0.1:8445"
)

func main() {
    // Create comment service.
    srv := service.New(
        service.Name(ServiceName),
        service.Address(ServerAddress),
    )

    // Register handlers.
    srv.Handle(handler.NewComment())

    // Run service.
    if err := srv.Run(); err != nil {
        logger.Fatal(err)
    }
}
</code></pre><p></p><p>And this is it, the comment service is ready. Next, we&#8217;ll need to start the comment service, but before that, using the same micro binary, log into the micro server. Then, start the comment service.</p><pre><code># username is admin, password is micro
$ /usr/local/bin/micro login
Enter username: admin
Enter password: micro

# run the comment service
$ /usr/local/bin/micro run .

# check if the service is running.
$ /usr/local/bin/micro status
NAME    VERSION SOURCE STATUS  BUILD UPDATED METADATA
comment latest  ...    running n/a   4s ago  owner=admin...</code></pre><p>If the status shows as error, then check the service logs.</p><pre><code>$ /usr/local/bin/micro logs comment</code></pre><p></p><p>Now, let&#8217;s see if the service responds to user HTTP requests. Note that micro gateway is running on localhost port 8080.</p><pre><code># List all comments in post `post1`
$ http --json --body \
  'http://localhost:8080/comment/list' post='post1'
{
    "comments": [
        {
            "author": "greycell",
            "created": "1604650806",
            "id": "830e9eef-251e-472a-b324-41545529ad76",
            "message": "this is a comment",
            "post": "post1"
        }
    ]
}


# Create a new comment.
$ http --json --body POST \
  'http://localhost:8080/comment/new' \
  post='post1' author='greycell' message='test comment'
{
    "comment": {
        "author": "greycell",
        "created": "1606763037",
        "id": "6c68e596-0e38-4ff2-80ad-48e6f45758a1",
        "message": "test comment",
        "post": "post1"
    }
}</code></pre><p>Here, the path in the API starts with <em><code>/comment/</code></em> to indicate to the micro gateway that the user is requesting a response from the comment service. The gateway automatically unmarshalled the json body in the request appropriately into the protobuf request message. The gateway also mapped the request to appropriate business handler in the comment service based on the endpoints <em><code>/comment/new</code></em> or <em><code>/comment/list</code></em>.</p><h2>Final Thoughts</h2><p>In this post, we briefly discussed about Micro and how we can easily setup micro gateway and a simple micro service. In the next post, we&#8217;ll look at how to setup custom plugins and profiles in micro gateway in order to provide custom features like authentication and rate limits for our HTTP endpoints.</p><p>If you&#8217;d like to learn more about Micro, checkout https://micro.mu</p><p>You can access the full code for this chapter here: https://github.com/abvarun226/microservice/tree/chapter01</p><p>I hope this was helpful and if you find any errors or issues in this post, please do let me know in the comments section below.</p><p>In case you would like to get notified about more articles like this, please subscribe.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.greycell.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.greycell.dev/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Using Ristretto to cache objects in Go]]></title><description><![CDATA[A short tutorial on how to use Dgraph's Go library Ristretto to cache objects]]></description><link>https://blog.greycell.dev/p/using-ristretto-to-cache-objects</link><guid isPermaLink="false">https://blog.greycell.dev/p/using-ristretto-to-cache-objects</guid><dc:creator><![CDATA[Bharghava Varun Ayada]]></dc:creator><pubDate>Sun, 29 Nov 2020 15:18:46 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/1221a5b9-b5a2-425b-a6ca-487f90448cbc_3842x5763.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my <a href="https://cryptic.substack.com/p/caching-objects-by-tags-in-go">previous article</a> on Greycell, I wrote about caching objects using tags and how it can make cache invalidation easier. I used Redis as a temporary store for our objects that needed to be cached. Although Redis is really good for this, I wanted an embedded key-value store in order to avoid additional dependency of maintaining a Redis server. When I explored further, I came across many embedded key-value store like <a href="https://github.com/golang/groupcache">groupcache</a>, <a href="https://github.com/VictoriaMetrics/fastcache">fastcache</a>, <a href="https://github.com/goburrow/cache">burrow cache</a> and Dgraph&#8217;s <a href="https://github.com/dgraph-io/ristretto">Ristretto</a>. Groupcache is an excellent distributed embedded caching library meant to replace a pool of independent Redis or Memcache nodes. Mailgun&#8217;s enhancement to <a href="https://github.com/mailgun/groupcache">groupcache</a> adds explicit key removal, key expiry and many more helpful features. I&#8217;d like to write an article about groupcache in the future, but for now I&#8217;ll settle for Ristretto.</p><p>Ristretto is a high performance, concurrent, memory-bound Go cache. It is contention-proof, scales well and provides consistently high hit-ratios. It is really easy to use with very good documentation and a detailed <a href="https://dgraph.io/blog/post/introducing-ristretto-high-perf-go-cache/">blog post</a> about it&#8217;s architecture and benchmarks.</p><p>This article provides a brief implementation of caching objects by tags using Ristretto. Let me first explain the strategy that I&#8217;ll use to keep track of tags and the cache keys that are tagged with a specific tag name.</p><h2>Implementation details</h2><p>Let&#8217;s say we want to tag a cache key with tag name <em>tag1</em>. What we want to do first is to create a set called <em>tag1</em>. We will then add the cache key to be tagged with <em>tag1</em> to this set. This way, when we want to invalidate a cache key by it&#8217;s tag name <em>tag1</em>, we will delete all cache keys that are members of the set <em>tag1</em>.</p><p>But unlike Redis, Ristretto is a very simple key-value store. There is no set data structure that the library itself provides. But it should be trivial to create a set data structure in Go using a map. Note that we only want to store strings in the set since cache keys are almost always going to be strings. So let&#8217;s create a simple set data structure that accepts strings as it&#8217;s members. </p><pre><code>// file: ./ds/set.go

package ds

import "sync"

// New StringSet.
func New() *StringSet {
    s := StringSet{
        d: make(map[string]struct{}),
    }
    return &amp;s
}

// We will set the value of the key in the map to this.
var special = struct{}{}

// StringSet struct.
type StringSet struct {
    l sync.RWMutex
    d map[string]struct{}
}

// Add method to add a member to the StringSet.
func (s *StringSet) Add(member string) {
    s.l.Lock()
    defer s.l.Unlock()

    s.d[member] = special
}

// Remove method to remove a member from the StringSet.
func (s *StringSet) Remove(member string) {
    s.l.Lock()
    defer s.l.Unlock()

    delete(s.d, member)
}

// IsMember method to check if a member is present in the StringSet.
func (s *StringSet) IsMember(member string) bool {
    s.l.RLock()
    defer s.l.RUnlock()

    _, found := s.d[member]
    return found
}

// Members method to retrieve all members of the StringSet.
func (s *StringSet) Members() []string {
    s.l.RLock()
    defer s.l.RUnlock()

    keys := make([]string, 0)
    for k := range s.d {
        keys = append(keys, k)
    }
    return keys
}

// Size method to get the cardinality of the StringSet.
func (s *StringSet) Size() int {
    s.l.RLock()
    defer s.l.RUnlock()

    return len(s.d)
}

// Clear method to remove all members from the StringSet.
func (s *StringSet) Clear() {
    s.l.Lock()
    defer s.l.Unlock()

    s.d = make(map[string]struct{})
}</code></pre><p></p><p>We can use this set data structure like this:</p><pre><code>s := ds.New()
s.Add("hello")
s.Add("world")
isMember := s.IsMember("hello") // returns true
members := s.Members() // returns all members of the set.</code></pre><p>We can add more methods to this set implementation, like union, intersection etc. But this should be sufficient for our use case here.</p><h2>Implement a cache store</h2><p>Now that we have our set data structure, let&#8217;s see how we can use this in our cache library. So, let&#8217;s initialise our Ristretto cache store.</p><pre><code>// file: ./cache/cache.go

package cache

import (
    "log"
    "sync"
    "time"

    "github.com/abvarun226/ristretto-cache/ds"
    "github.com/dgraph-io/ristretto"
)

// New initialises a new cache store.
func New() *Store {
    c, errNew := ristretto.NewCache(&amp;ristretto.Config{
        MaxCost:     1 &lt;&lt; 30, // 1GB
        NumCounters: 1e7, // 10M
        BufferItems: 64,
    })
    if errNew != nil {
        log.Fatalf("failed to initialise cache: %v", errNew)
    }
    return &amp;Store{store: c}
}

// Store is our struct for cache store.
type Store struct {
    l     sync.RWMutex
    store *ristretto.Cache
}</code></pre><p></p><p>The above code snippet is part of our cache library and it initialises the Ristretto cache and returns our local cache store. Based on the settings used, it can store upto 1GB of data in the cache. We&#8217;ll now add methods that will help us interact with this local cache store.</p><pre><code>// file: ./cache/cache.go

// SetByTags method to set cache by given tags.
func (s *Store) SetByTags(key, value string, expiry time.Duration, tags []string) {
    s.l.Lock()
    defer s.l.Unlock()

    for _, tag := range tags {
        set := ds.New()
        if v, found := s.store.Get(tag); found {
            set = v.(*ds.StringSet)
        }
        set.Add(key)
        s.store.Set(tag, set, 1)
    }

    s.store.SetWithTTL(key, value, 1, expiry)
}</code></pre><p></p><p><em>SetByTags</em> method will cache the object and also tag it with the tag names provided as a list of strings. You can use any of these strings to invalidate the cache object. We&#8217;ll use the locks to ensure that two or more requests don&#8217;t update the same set at the same time. </p><p>Now let&#8217;s look at the code to invalidate cache using the tags.</p><pre><code>// file: ./cache/cache.go

// Invalidate method to invalidate cache with given tags.
func (s *Store) Invalidate(tags []string) {
    s.l.Lock()
    defer s.l.Unlock()

    keys := make([]string, 0)
    for _, tag := range tags {
        set := ds.New()
        if v, found := s.store.Get(tag); found {
            set = v.(*ds.StringSet)
        }
        keys = append(keys, set.Members()...)
        keys = append(keys, tag)
    }

    for _, k := range keys {
        s.store.Del(k)
    }
}</code></pre><p></p><p>Again, here we use locks to synchronise any changes to the set with the given tag name. This method will get all the cache keys associated with the tag name and delete the keys from the cache store, along with the set.</p><p>As for other methods to interact with the cache store, it is pretty straight forward.</p><pre><code>// file: ./cache/cache.go

// Get method to retrieve the value of a key. If not present, returns false.
func (s *Store) Get(key string) (string, bool) {
    var value string
    val, found := s.store.Get(key)
    if found {
        value = val.(string)
    }
    return value, found
}

// Delete method to delete a key from cache.
func (s *Store) Delete(key string) {
    s.store.Del(key)
}

// Close method clear and then close the cache store.
func (s *Store) Close() {
    s.store.Clear()
    s.store.Close()
}
</code></pre><p><br>Here is how you can use the cache store.</p><pre><code>// file: ./example.go

// create a new cache.
c := cache.New()

key1 := "key1"
val1 := "value1"
tags := []string{"tag1", "tag2"}

// cache value and set the tags along with expiry.
c.SetByTags(key1, val1, expiry, tags)

// Do some work here.
time.Sleep(5 * time.Millisecond)

// check if key exists in cache.
if res, found := c.Get(key1); found {
    fmt.Printf("found the key `%s`: `%s`", key1, res)
} else {
    fmt.Printf("key `%s` not found", key1)
}

// Invalidate cache using a tag.
c.Invalidate(tags[0])

// now, check if key exists in cache.
if res, found := c.Get(key1); found {
    fmt.Printf("found the key `%s`: `%s`", key1, res)
} else {
    fmt.Printf("key `%s` not found", key1)
}
</code></pre><p>The output of the above snippet should look like this:</p><pre><code>found the key `key1`: `value1`
key `key1` not found</code></pre><p></p><h2>Final Thoughts</h2><p>Ristretto is quite performant, probably faster than using Redis, since it avoids a network call to a standalone Redis server. It is pretty easy to implement a caching strategy using Ristretto, but if you want the same cache objects to be available across different processes or instances, then you&#8217;d need to use a Redis server or probably a distributed caching library like <a href="https://github.com/mailgun/groupcache">Groupcache</a>. I&#8217;ll write an article about how we can use Groupcache in a future post.</p><p>You can access the code here: <a href="https://github.com/abvarun226/ristretto-cache">https://github.com/abvarun226/ristretto-cache</a></p><p>I hope this was helpful and if you find any errors or issues in this post, please do let me know in the comments section below.</p><p>In case you would like to get notified about more articles like this, please subscribe.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.greycell.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.greycell.dev/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Caching objects by tags in Go]]></title><description><![CDATA[A short tutorial on how one can cache objects and tag them to make invalidation easier.]]></description><link>https://blog.greycell.dev/p/caching-objects-by-tags-in-go</link><guid isPermaLink="false">https://blog.greycell.dev/p/caching-objects-by-tags-in-go</guid><dc:creator><![CDATA[Bharghava Varun Ayada]]></dc:creator><pubDate>Sat, 28 Nov 2020 07:52:46 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/0495fcec-f2ff-43b3-8350-6d3e788759fd_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Caching has an important role to play in a system design. It is primarily used to speed up information retrieval from a service and reduce the load on a database engine, the primary source of truth for a service. Data that is frequently requested by users are stored in a in-memory key-value store, like <a href="https://redis.io/">Redis</a>, to speed up subsequent look up of the same data. In this post, I want to specifically talk about cache invalidation and provide an implementation in Go for invalidating cache using tags.</p><h2>Cache Invalidation</h2><p>When frequently accessed data which is stored in a cache is updated, it is necesssary for the application to evict the stale data from the cache. So when subsequent requests for the same data is received by the application, it will retrieve fresh data from the database and store it in the cache. This way, users will never receive stale data from the application. The process of cache invalidation can be made easier by tagging data with specific tags when storing in the cache. This way, when the data needs to be invalidated, we can use the tag names to evict them from the cache.</p><p>Here is an implementation of caching records by tags. We will use Redis and <code>github.com/go-redis/redis</code> package.</p><pre><code>package cache

import (
    "github.com/go-redis/redis/v7"
)

type cache struct {
    client redis.Client
}

// SetByTags to cache objects and then tag them.
func (c *cache) SetByTags(ctx context.Context, key string, val interface{}, tags []string, expiry time.Duration) error {
&#9;pipe := c.client.TxPipeline()
&#9;for _, tag := range tags {
&#9;&#9;pipe.SAdd(tag, key)
&#9;&#9;pipe.Expire(tag, expiry)
&#9;}

&#9;pipe.Set(key, b, expiry)

&#9;_, errExec := pipe.Exec()
&#9;return errExec
}

// Invalidate to invalidate cached objects with given tags.
func (c *cache) Invalidate(ctx context.Context, tags []string) {
&#9;keys := make([]string, 0)
&#9;for _, tag := range tags {
&#9;&#9;k, _ := c.client.SMembers(tag).Result()
&#9;&#9;keys = append(keys, tag)
&#9;&#9;keys = append(keys, k...)
&#9;}
&#9;c.client.Del(keys...)
}
</code></pre><p></p><p>In <code>SetByTags</code> method, we maintain a <code>set</code> data structure for each tag provided. We then add the given cache key into these sets. When invalidating cache by tags, we get all the members, i.e. cache keys, for the given tags and delete them from Redis.</p><p>Example usage:</p><pre><code><code>
tags := []string{"post1", "post2"}
value := "blog data"

key1 := "blog:one:post1"
key2 := "blog:one:post2"

// Set cache by tags here:
cache.SetByTags(ctx, key1, value, tags, 24 * time.Hour)
cache.SetByTags(ctx, key2, value, tags, 24 * time.Hour)


// Invalidate cache by tag "post1" here.
// Both key1 and key2 will be evicted, since both were tagged with "post1".
cache.Invalidate(ctx, []string{"post1"})</code></code></pre><h2>References</h2><ul><li><p><a href="https://redis.io">https://redis.io</a></p></li><li><p><a href="https://github.com/go-redis/redis.git">https://github.com/go-redis/redis.git</a></p></li></ul><p></p><p>Subscribe now to immediately get notified when more such articles are published.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.greycell.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.greycell.dev/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Software development, productivity and everything to help you improve in bite-size]]></title><description><![CDATA[Welcome to Greycell's Newsletter by me, Bharghava Varun Ayada. I'm a software engineer, a Go and Python enthusiast. I also like competitive programming and backend development. Sign up now so you don&#8217;t miss the first issue. In the meantime, tell your friends]]></description><link>https://blog.greycell.dev/p/coming-soon</link><guid isPermaLink="false">https://blog.greycell.dev/p/coming-soon</guid><dc:creator><![CDATA[Bharghava Varun Ayada]]></dc:creator><pubDate>Thu, 08 Oct 2020 18:19:10 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/601b80fc-5f18-456a-a945-2e02d036be48_775x1033.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to Greycell&#39;s Newsletter by me, Bharghava Varun Ayada. I&#39;m a software engineer, a Go and Python enthusiast. I also like competitive programming and backend development.</p><p>Sign up now so you don&#8217;t miss the first issue.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.greycell.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.greycell.dev/subscribe?"><span>Subscribe now</span></a></p><p>In the meantime, <a href="https://blog.greycell.dev/p/coming-soon?utm_source=substack&utm_medium=email&utm_content=share&action=share">tell your friends</a>!</p>]]></content:encoded></item></channel></rss>