Go - Concurrency Pipeline

Pipeline is yet another common pattern we see in concurrent programming. Stages for a data processing pipline can be for example, Read the file Process the data Transform the data Write the data to another file Let’s check an example of such a pipeline in Golang. package pipeline import ( "fmt" "time" ) type FileData struct { src string data string } // readData stage where we read the file // it returns a channel of FileData to use in the next stage func readData(filePaths []string) <-chan FileData { out := make(chan FileData) go func() { for _, path := range filePaths { time.Sleep(1 * time.Second) // Simulate file read out <- FileData{src: path, data: "data"} } close(out) }() return out } // processData stage where we process the data // it returns a channel of processed FileData to use in the next stage func processData(in <-chan FileData) <-chan FileData { out := make(chan FileData) go func() { for data := range in { time.Sleep(1 * time.Second) // Simulate data processing out <- FileData{src: data.src, data: data.data + " processed"} } close(out) }() return out } // transformData stage where we transform the data // it returns a channel of transformed FileData to use in the next stage func transformData(in <-chan FileData) <-chan FileData { out := make(chan FileData) go func() { for data := range in { time.Sleep(1 * time.Second) // Simulate data transformation out <- FileData{src: data.src, data: data.data + " transformed"} } close(out) }() return out } // writeData stage where we write the data to a file func writeData(in <-chan FileData) bool { for data := range in { time.Sleep(1 * time.Second) // Simulate file write fmt.Printf("Writing data to %s\n", data.src) } return true } // RunPipeline runs the pipeline // each stage is a go routine // and they are connected via channels func RunPipeline(filePaths []string) { readDataCh := readData(filePaths) processDataCh := processData(readDataCh) transformDataCh := transformData(processDataCh) writeData(transformDataCh) } In the above example, we have implemented a pipeline with 4 stages. Each stage is a go routine and they are connected via channels. ...

Go - Concurrency Workerpool

Golang has excellent support for concurrency. Let’s see how to implement a common pattern - worker pool in Golang. Worker pool A worker pool is a collection of threads that are waiting for tasks to be assigned. We can limit the number of concurrent operations at a time with this approach. Usually starting a new concurrent thread or routine is not practical for every task especially when the number of tasks is large. ...

Go - Overview

Golang, or Go, is a statically typed, compiled programming language designed for simplicity and efficiency. It is commonly used for backend systems, cloud services, and DevOps tools due to its robust standard library and support for concurrent programming. Go follows a procedural and concurrent programming paradigm, making it ideal for modern computing challenges. Few things I like about go are The seperation of data (struct) and behavior (methods). This makes the code more readable and maintainable. Easiness to write concurrent code. Go routines and channels are very easy to use and understand. Zero values instead of null. This makes the code more predictable and less error prone. Few things I dislike about go are ...

GO + HTMX - A Crud implementation

As I was exploring htmx, I wanted to try out an interactive workflow with it. A classic example is a data list, modal to create / update data and a delete. Setup with Gofiber Initialize go module and install dependencies go mod init htmx-crud go get github.com/gofiber/fiber/v2 go get github.com/gofiber/template/html/v2 Setup Server html template engine main.go package main import ( "log" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/log" "github.com/gofiber/template/html/v2" ) func main() { // Setup html template engine engine := html.New("./views", ".html") app := fiber.New(fiber.Config{ Views: engine, }) // Get request to render index page app.Get("/", func(c *fiber.Ctx) error { // Render index template return c.Render("index", nil, "layouts/main") }) // Start server log.Fatal(app.Listen(":3000")) } This is a simple server setup with gofiber. We are using html template engine to render the html. ...

Go - Template

Let’s check the Go template syntax and essentials. Placeholders The {{ }} is used to denote a placeholder. It can contain a variable, function, control statments like if, for, range etc. a variant of placeholder is {{- -}} which is used to trim the whitespace before the placeholder, {{- trims the whitespace before the placeholder and -}} trims the whitespace after the placeholder. Variables Variables are defined using the {{ $variableName := value }} syntax. The variable can be used in the template using {{ $variableName }}. ...

Go - Options Pattern

In go, it’s really useful pattern to pass options to functions. It’s a common usecase to have optional arguments to a function. I use it mostly to initialize something with default values and optionally override them. Primary idea is to use variadic arguments to pass options to a function. eg: func NewService(options ...ServiceOption) *Service { ... } options ...ServiceOption is the variadic argument. There are two ways to do this. Using a struct with all the options as a field. Using functions with each option value as an argument. Using a struct to pass options Example usage package main type ServiceConfig struct { Color string Size int Type string } func main() { // Initialize with default values s := NewService() // Override some of the default values s = NewService(&ServiceConfig{ Color: "red", Size: 10, }) } How to setup this options pattern You can use config... to accept any number of args in a function. Let’s create a config section with init config function. ...

Go - Setup simple gofiber server.

Gofiber is a fast lightweight web framework with simple api, inspired by expressjs. The simplicity of the api makes it easy to get started with. It’s also fast and has a lot of features. Setup Create a new project Create a new directory and initialize a go module. go mod init github.com/adharshmk96/gofiber-init Install gofiber go get -u github.com/gofiber/fiber/v2 a main.go with hello world touch main.go package main import "github.com/gofiber/fiber/v2" func main() { // gofiber with default settings app := fiber.New() // handler for root path app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello, World 👋!") }) // start server on port 3000 app.Listen(":3000") fmt.Println("Server started on port 3000") } Dockerfile for building the app ...

Go - Dependancy Injection pattern.

Dependancy injection is a pattern where we pass down dependancies to initializers. This pattern is used to avoid global variables and to make code more testable. For example in go, you can create a service which depends on a repository and pass the repository to the service initializer. Suppose you have a directory like this server ├─entities │ ├─data.go │ └─server.go ├─handler │ └─handler.go ├─service │ └─service.go └─storage └─storage.go main.go suppose you have a User struct in data.go ...