ryan961 / memo

https://github.com/ryan961/memo/issues
Creative Commons Zero v1.0 Universal
0 stars 0 forks source link

Golang Toolkits #2

Open ryan961 opened 4 months ago

ryan961 commented 4 months ago

πŸ“– Abstract

Hi~πŸ‘‹πŸΌ This is a collection of Go-related packages and tools that I frequently use, may potentially use, and ones that I find intriguing.

Recommended Related Resources:

πŸ—‚οΈ Packages

πŸ› οΈ Tools

More to come… :)

[_**⏫️ Back to home**_](https://github.com/ryan961/memo/issues/8)
ryan961 commented 4 months ago

πŸ“¦ Package

github.com/gin-gonic/gin

πŸ€–οΈ AI Intro

Gin is an HTTP web framework written in Go (Golang) that features a Martini-like API with performance that is up to 40 times faster, thanks to httprouter. It provides a rich set of functionalities to quickly develop high-performing web applications. It's well-suited for building RESTful APIs with features like middleware support, JSON validation, and rendering, among others.

πŸ“ Example

package main

import "github.com/gin-gonic/gin"

func main() {
    // Create a new Gin router instance
    r := gin.Default()

    // Define a simple GET route
    r.GET("/ping", func(c *gin.Context) {
        // Respond with a JSON object
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    // Start the server on port 8080
    r.Run(":8080") // Listen and serve on localhost:8080
}

🧭 Related

πŸ”– Note

Gin is widely regarded as the most renowned web application framework within the Go ecosystem. It was the first web framework I utilized and the one I've used most frequently since I began to learn Go. With its comprehensive ecosystem, outstanding performance, and beginner-friendly approach, it stands out as an excellent choice for developers entering the Go language domain.

Additionally, there are several highly recognized web frameworks, including:

One should select the most suitable web framework based on individual preferences and practices.

HTTP routing in the standard library is now more expressive πŸŽ‰.The patterns used by net/http.ServeMux have been enhanced to accept methods and wildcards.You can learn more from the links:

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 4 months ago

πŸ“¦ Package

github.com/go-chi/chi/v5

πŸ€–οΈ AI Intro

Chi is a lightweight routing package for Go, emphasizing simplicity and composability. It builds on the standard net/http package, providing a rich routing API that allows developers to write declarative routes that are clean and easy to understand. It supports URL parameters, properly cascades routes, and has built-in middleware for tasks such as routing, throttling, and authentication.

πŸ“ Example

package main

import (
    "net/http"

    "github.com/go-chi/chi/v5"
)

func main() {
    router := chi.NewRouter()

    // A simple GET route
    router.Get("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Home Page"))
    })

    // URL parameter route
    router.Get("/users/{userID}", func(w http.ResponseWriter, r *http.Request) {
        userID := chi.URLParam(r, "userID")
        w.Write([]byte("User ID: " + userID))
    })

    // Start server
    http.ListenAndServe(":8080", router)
}

🧭 Related

πŸ”– Note

In comparison to Gin, Chi provides a more lightweight, closer-to-native Go HTTP routing framework. Furthermore, it offers a more user-friendly routing builder than both Go's native net/http and the virtually native Gorilla/mux, which is the primary reason for choosing it. However, for those familiar with Gin, certain functionalities like parameter binding and response returning in Chi(github.com/go-chi/render) can seem rather basic. Thus, I've created a simplified wrapper using github.com/gin-gonic/gin/binding and github.com/gin-gonic/gin/render to streamline development. I plan to share my work when time permits.

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 4 months ago

πŸ“¦ Package

github.com/ServiceWeaver/weaver

πŸ€–οΈ AI Intro

Service Weaver is a programming framework designed to simplify the process of creating, deploying, and managing distributed applications. The core of a Service Weaver application is composed of components, which are represented as Go interfaces. These components interact with each other via method calls, removing the need for manual networking or serialization code.

Service Weaver offers libraries to assist in logging, metrics, tracing, routing, testing, and more. The deployment of a Service Weaver application is very straightforward, requiring only one command. The system automatically manages the bifurcation of your binary along component boundaries, facilitating the running of different components on different machines.

Service Weaver handles replication, autoscaling, and co-location of distributed components, while also managing the networking details, ensuring seamless communication between various components and with clients.

πŸ“ Example

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ServiceWeaver/weaver"
)

func main() {
    if err := weaver.Run(context.Background(), serve); err != nil {
        log.Fatal(err)
    }
}

// app is the main component of the application. weaver.Run creates
// it and passes it to serve.
type app struct{
    weaver.Implements[weaver.Main]
}

// serve is called by weaver.Run and contains the body of the application.
func serve(context.Context, *app) error {
    fmt.Println("Hello")
    return nil
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 4 months ago

πŸ“¦ Package

github.com/go-kratos/kratos

πŸ€–οΈ AI Intro

Kratos is a robust Go microservices framework designed to build scalable and maintainable systems. It includes a variety of out-of-the-box features such as service discovery, configuration management, messaging, and resiliency strategies for dealing with failures. It follows the best practices and conventions for microservice architecture, simplifying both the bootstrapping and the development of service-oriented applications.

πŸ“ Example

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 4 months ago

πŸ“¦ Package

github.com/cloudwego/hertz

πŸ€–οΈ AI Intro

Hertz is a Go web framework designed for building microservices with a focus on high performance and extensibility. It simplifies network programming and has built-in support for creating HTTP1.x/HTTP2 servers or clients. Inspired by other popular frameworks such as Echo and Gin, Hertz provides developers with a robust set of tools to develop efficient and safe web services quickly.

πŸ“ Example

package main

import (
    "context"

    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/cloudwego/hertz/pkg/common/utils"
    "github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main() {
    h := server.Default()

    h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
            ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
    })

    h.Spin()
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 4 months ago

πŸ“¦ Package

github.com/cloudwego/kitex

πŸ€–οΈ AI Intro

Kitex is a high-performance and scalable network framework for building efficient communication systems. It is specially designed to handle large volumes of network traffic with minimal overhead. Kitex achieves this through optimizations at the connection, protocol, and application layers.

Kitex's functionality includes message routing, load balancing, and fault tolerance among others. It facilitates rapid development of services through the provision of essential tools and libraries that solve common network programming tasks. With Kitex, developers can focus on application logic, rather than having to grapple with low-level network details.

In summary, Kitex is a crucial tool that enables developers to build robust and efficient network-based applications, providing a balance of speed, flexibility, and scalability.

πŸ“ Example

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 4 months ago

🎯 Tool

go install github.com/dkorunic/betteralign/cmd/betteralign@latest

πŸ€–οΈ AI Intro

Betteralign is a modification of the official Go fieldalignment tool, designed to identify and optimise memory usage of structs by rearranging their fields. Its functionality is similar to the original, but it introduces several changes. It ignores generated files, skips over test files, and bypasses structs marked with the comment 'betteralign:ignore'. Importantly, this version is able to preserve comments such as field comments and doc comments.

Although the comment position heuristics feature is still under development, betteralign provides reliable atomic file I/O, with a promise of not corrupting or losing content on rewrite. It also outperforms its predecessor in testing and is compatible with environments with limited CPU or memory resources, including Docker containers, K8s containers, LXC, LXD, etc.

🏹 Usage

main.go

package main

import "unsafe"

type UserName struct {
    name string
    // ZST [0]func() makes the surrounding type incomparable
    _phantom [0]func()
}

func main() {
    println(unsafe.Sizeof(UserName{}), unsafe.Sizeof("")) // 24 16
}
$ go run main.go
24 16

To get all recommendations on your project:

$ betteralign ./...
main.go:5:15: struct of size 24 could be 16

To automatically fix your project files, while ignoring test and generated files:

$ betteralign -apply ./...
main.go:5:15: struct of size 24 could be 16
$ go run main.go
16 16
$ cat main.go | grep -A 4 'type UserName struct'
type UserName struct {
        // ZST [0]func() makes the surrounding type incomparable
        _phantom [0]func()
        name     string
}

🧭 Related

πŸ”– Note

In daily programming, struct is frequently used to group fields (that may be of different types) for creating model objects, providing function parameters and return values, and managing data with complex structures. Often, the impact of field sequencing within a struct on memory usage (memory fragmentation) is overlooked. This can be due to a variety of reasons including, but not limited to, demanding a comprehension understanding of the underlying memory model amongst developers, whether the need for extreme performance tailored to the current business context and considering the trade-off between significant effort for trivial memory usage reduction at the current stage. The betteralign tool now provides a convenient solution to this problem.

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 4 months ago

🎯 Tool

go install golang.org/x/tools/cmd/deadcode@latest

πŸ€–οΈ AI Intro

The deadcode tool is a static analysis utility that is part of the larger Go toolchain found under golang.org/x/tools. It is used to identify parts of the code that are never used, otherwise known as dead code. Removing such code can lead to more maintainable and cleaner code bases. The tool is easy to use and can be integrated into various stages of the development process.

🏹 Usage

main.go

package main

import "fmt"

func main() {
    var g Greeter
    g = Helloer{}
    g.Greet()
}

type Greeter interface{ Greet() }

type Helloer struct{}
type Goodbyer struct{}

var _ Greeter = Helloer{}  // Helloer  implements Greeter
var _ Greeter = Goodbyer{} // Goodbyer implements Greeter

func (Helloer) Greet()  { hello() }
func (Goodbyer) Greet() { goodbye() }

func hello()   { fmt.Println("hello") }
func goodbye() { fmt.Println("goodbye") }

When we execute it, it says hello:

$ go run .
hello

When we run deadcode on this program, the tool tells us that the goodbye function and the Goodbyer.Greet method are both unreachable:

$ deadcode .
greet.go:23: unreachable func: goodbye
greet.go:20: unreachable func: Goodbyer.Greet

The tool can also explain why the hello function is live. It responds with a chain of function calls that reaches hello, starting from main:

$ deadcode -whylive=example.com/greet.hello .
                  example.com/greet.main
dynamic@L0008 --> example.com/greet.Helloer.Greet
 static@L0019 --> example.com/greet.hello

The output is designed to be easy to read on a terminal, but you can use the -json or -f=template flags to specify richer output formats for consumption by other tools.

🧭 Related

πŸ”– Note

The Go team recently published an article titled 'Finding Unreachable Functions with Deadcode' on their official blog. The article provides detailed examples of using the deadcode tool and explains how it works. After reading the article, I felt quite motivated to apply this tool to my project. I began by executing go install ..., followed by keying in deadcode ., and confirming the command. As expected, a considerable number of unreachable functions were revealed, a result that does signify an increased workload soon.

There are many static check tools in Golang, one of which is golangci-lint, that I currently utilize. It includes a linter called unused(which checks Go code for unused constants, variables, functions, and types), it only identifies some unused functions that can be directly invoked. On the contrary, the deadcode tool can identify functions that are part of your project’s source code but can never be reached in any execution. Nonetheless, the author of the article emphasizes that "All static analysis tools inevitably yield imperfect approximations of the potential dynamic behaviors of the target program." This implies that the deadcode is included in this realm of imperfection. For example, it currently falls short of identifying functions specifically called through assembly code and function aliases engendered by the go:linkname directive.

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 3 months ago

πŸ“¦ Package

github.com/redis/go-redis/v9

πŸ€–οΈ AI Intro

The go-redis/v9 package is an advanced, feature-rich client for Go that allows comprehensive communication with Redis. Its simplicity and power make it a popular choice among developers when working with Redis in Go. The package supports all the Redis commands and also provides technologies such as pipelining and transactions along with cluster support.

πŸ“ Example

import (
    "context"
    "fmt"

    "github.com/redis/go-redis/v9"
)

var ctx = context.Background()

func ExampleClient() {
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    err := rdb.Set(ctx, "key", "value", 0).Err()
    if err != nil {
        panic(err)
    }

    val, err := rdb.Get(ctx, "key").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("key", val)

    val2, err := rdb.Get(ctx, "key2").Result()
    if err == redis.Nil {
        fmt.Println("key2 does not exist")
    } else if err != nil {
        panic(err)
    } else {
        fmt.Println("key2", val2)
    }
    // Output: key value
    // key2 does not exist
}

🧭 Related

πŸ”– Note

go-redis is a redis client in Go and maintained by the official Redis organization, benefiting from robust community support. It offers support for various flavors of Redis(including but not limited to, standalone instances, clusters, sentinels, and master-slave setups), automatic connection pooling, full compatibility with all Redis commands, and features a well-structured API design which makes it an unquestionable choice.

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/sourcegraph/conc

πŸ€–οΈ AI Intro

conc tries to solve common problems in handling concurrency in Go. It provides safer versions of sync.WaitGroup and sync.Pool with error handling and limitations. It also offers utilities for running tasks in an ordered stream and for concurrent mapping and iteration over slices. Moreover, it includes a panics.Catcher feature to gracefully manage panics in goroutines to avoid crashes.

πŸ“ Example

// Spawn a set of goroutines and waiting for them to finish
func main() {
    var wg conc.WaitGroup
    wg.Go(doSomethingThatMightPanic)
    // panics with a nice stacktrace
    wg.Wait()
}

// Process each element of a stream in a static pool of goroutines
func process(stream chan int) {
    p := pool.New().WithMaxGoroutines(10)
    for elem := range stream {
        elem := elem
        p.Go(func() {
            handle(elem)
        })
    }
    p.Wait()
}

// Process each element of a slice in a static pool of goroutines
func process(values []int) {
    iter.ForEach(values, handle)
}

// Concurrently map a slice
func concMap(
    input []int,
    f func(*int) int,
) []int {
    return iter.Map(input, f)
}

// Process an ordered stream concurrently
func mapStream(
    in chan int,
    out chan int,
    f func(int) int,
) {
    s := stream.New().WithMaxGoroutines(10)
    for elem := range in {
        elem := elem
        s.Go(func() stream.Callback {
            res := f(elem)
            return func() { out <- res }
        })
    }
    s.Wait()
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/shopspring/decimal

πŸ€–οΈ AI Intro

The shopspring/decimal Go package is an arbitrary-precision fixed-point decimal number library. The decimal library can represent numbers with a maximum of 2^31 digits after the decimal point. It supports operations such as addition, subtraction, and multiplication with no loss of precision, and division with specified precision. It also caters to Database/sql serialization/deserialization along with JSON and XML serialization/deserialization.

πŸ“ Example

package main

import (
    "fmt"

    "github.com/shopspring/decimal"
)

func main() {
    price, err := decimal.NewFromString("136.02")
    if err != nil {
        panic(err)
    }

    quantity := decimal.NewFromInt(3)

    fee, _ := decimal.NewFromString(".035")
    taxRate, _ := decimal.NewFromString(".08875")

    subtotal := price.Mul(quantity)

    preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1)))

    total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1)))

    fmt.Println("Subtotal:", subtotal)                      // Subtotal: 408.06
    fmt.Println("Pre-tax:", preTax)                         // Pre-tax: 422.3421
    fmt.Println("Taxes:", total.Sub(preTax))                // Taxes: 37.482861375
    fmt.Println("Total:", total)                            // Total: 459.824961375
    fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/robfig/cron/v3

πŸ€–οΈ AI Intro

This Go package is a library for scheduling jobs. It's made with simplicity in mind and uses the standard cron syntax. It offers its built-in function-based Job type, but users can supply their own Job implementents as well.

πŸ“ Example

package main

import (
    "fmt"
    "time"

    "github.com/robfig/cron/v3"
)

func main() {
    c := cron.New(cron.WithSeconds())
    c.AddFunc("*/5 * * * * *", func() {
        fmt.Println("Every 5 seconds job", time.Now().Format(time.RFC3339))
    })
    c.Start()

    for {
        time.Sleep(time.Second)
        fmt.Println("Current time", time.Now().Format(time.RFC3339))
    }
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

go.uber.org/zap

πŸ€–οΈ AI Intro

zap is a structured, leveled logging library designed with both low-latency applications and the flexibility of structured logging in mind. It includes a reflection-free, zero-allocation JSON encoder, and provides options for level logging and encoding. Built with multi-threaded performance considerations and with a consistent, predictable, and easy-to-use API, it's an excellent option for applications of any size.

πŸ“ Example

package main

import (
    "go.uber.org/zap"
)

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync() // flushes buffer, if any
    sugar := logger.Sugar()
    sugar.Infow("failed to fetch URL",
        // Structured context as loosely typed key-value pairs.
        "url", "http://example.com",
        "attempt", 3,
        "backoff", time.Second,
    )
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/oschwald/geoip2-golang

πŸ€–οΈ AI Intro

geoip2-golang gives you the ability to use MaxMind GeoIP2 and GeoLite2 databases efficiently and easily. This helps you fetch geographical location data based on IP addresses with high accuracy, such as the city, country, and continent.

πŸ“ Example

package main

import (
    "fmt"
    "log"
    "net"

    "github.com/oschwald/geoip2-golang"
)

func main() {
    db, err := geoip2.Open("GeoIP2-City.mmdb")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    // If you are using strings that may be invalid, check that ip is not nil
    ip := net.ParseIP("81.2.69.142")
    record, err := db.City(ip)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Portuguese (BR) city name: %v\n", record.City.Names["pt-BR"])
    if len(record.Subdivisions) > 0 {
        fmt.Printf("English subdivision name: %v\n", record.Subdivisions[0].Names["en"])
    }
    fmt.Printf("Russian country name: %v\n", record.Country.Names["ru"])
    fmt.Printf("ISO country code: %v\n", record.Country.IsoCode)
    fmt.Printf("Time zone: %v\n", record.Location.TimeZone)
    fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude)
    // Output:
    // Portuguese (BR) city name: Londres
    // English subdivision name: England
    // Russian country name: ВСликобритания
    // ISO country code: GB
    // Time zone: Europe/London
    // Coordinates: 51.5142, -0.0931
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/jinzhu/now

πŸ€–οΈ AI Intro

now is a Go package that provides a wide array of functionalities to simplify work with time, beyond what's offered by Go's inbuilt time package. It boasts a robust feature set that allows you to parse, format and manipulate time with ease. This package is highly useful to deal with tasks such as getting the start/end of a day/week/month/year, or calculating the duration between two time points.

πŸ“ Example

import "github.com/jinzhu/now"

time.Now() // 2013-11-18 17:51:49.123456789 Mon

now.BeginningOfMinute()        // 2013-11-18 17:51:00 Mon
now.BeginningOfHour()          // 2013-11-18 17:00:00 Mon
now.BeginningOfDay()           // 2013-11-18 00:00:00 Mon
now.BeginningOfWeek()          // 2013-11-17 00:00:00 Sun
now.BeginningOfMonth()         // 2013-11-01 00:00:00 Fri
now.BeginningOfQuarter()       // 2013-10-01 00:00:00 Tue
now.BeginningOfYear()          // 2013-01-01 00:00:00 Tue

now.EndOfMinute()              // 2013-11-18 17:51:59.999999999 Mon
now.EndOfHour()                // 2013-11-18 17:59:59.999999999 Mon
now.EndOfDay()                 // 2013-11-18 23:59:59.999999999 Mon
now.EndOfWeek()                // 2013-11-23 23:59:59.999999999 Sat
now.EndOfMonth()               // 2013-11-30 23:59:59.999999999 Sat
now.EndOfQuarter()             // 2013-12-31 23:59:59.999999999 Tue
now.EndOfYear()                // 2013-12-31 23:59:59.999999999 Tue

now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday
now.EndOfWeek()                // 2013-11-24 23:59:59.999999999 Sun

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/jinzhu/copier

πŸ€–οΈ AI Intro

copier offers great utility when dealing with complex data structures in Go. It allows you to duplicate structures, even when dealing with nested fields. This is especially useful when the depth or complexity of the given structure is high or when you need to manage data with many levels of nested fields.

πŸ“ Example

package main

import (
    "fmt"

    "github.com/jinzhu/copier"
)

type User struct {
    Name        string
    Role        string
    Age         int32
    EmployeeCode int64 `copier:"EmployeeNum"` // specify field name

    // Explicitly ignored in the destination struct.
    Salary   int
}

func (user *User) DoubleAge() int32 {
    return 2 * user.Age
}

// Tags in the destination Struct provide instructions to copier.Copy to ignore
// or enforce copying and to panic or return an error if a field was not copied.
type Employee struct {
    // Tell copier.Copy to panic if this field is not copied.
    Name      string `copier:"must"`

    // Tell copier.Copy to return an error if this field is not copied.
    Age       int32  `copier:"must,nopanic"`

    // Tell copier.Copy to explicitly ignore copying this field.
    Salary    int    `copier:"-"`

    DoubleAge int32
    EmployeeId int64 `copier:"EmployeeNum"` // specify field name
    SuperRole string
}

func (employee *Employee) Role(role string) {
    employee.SuperRole = "Super " + role
}

func main() {
    var (
        user      = User{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 200000}
        users     = []User{{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 100000}, {Name: "jinzhu 2", Age: 30, Role: "Dev", Salary: 60000}}
        employee  = Employee{Salary: 150000}
        employees = []Employee{}
    )

    copier.Copy(&employee, &user)

    fmt.Printf("%#v \n", employee)
    // Employee{
    //    Name: "Jinzhu",           // Copy from field
    //    Age: 18,                  // Copy from field
    //    Salary:150000,            // Copying explicitly ignored
    //    DoubleAge: 36,            // Copy from method
    //    EmployeeId: 0,            // Ignored
    //    SuperRole: "Super Admin", // Copy to method
    // }

    // Copy struct to slice
    copier.Copy(&employees, &user)

    fmt.Printf("%#v \n", employees)
    // []Employee{
    //   {Name: "Jinzhu", Age: 18, Salary:0, DoubleAge: 36, EmployeeId: 0, SuperRole: "Super Admin"}
    // }

    // Copy slice to slice
    employees = []Employee{}
    copier.Copy(&employees, &users)

    fmt.Printf("%#v \n", employees)
    // []Employee{
    //   {Name: "Jinzhu", Age: 18, Salary:0, DoubleAge: 36, EmployeeId: 0, SuperRole: "Super Admin"},
    //   {Name: "jinzhu 2", Age: 30, Salary:0, DoubleAge: 60, EmployeeId: 0, SuperRole: "Super Dev"},
    // }

    // Copy map to map
    map1 := map[int]int{3: 6, 4: 8}
    map2 := map[int32]int8{}
    copier.Copy(&map2, map1)

    fmt.Printf("%#v \n", map2)
    // map[int32]int8{3:6, 4:8}
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

🎯 Tool

github.com/davecgh/go-spew

πŸ€–οΈ AI Intro

go-spew package provides a deep pretty printer for Go data structures to help in debugging. It offers a handy and straightforward approach to dump variables for debugging while doing development.

🏹 Usage

The following is an example usage of go-spew:

package main

import (
    "github.com/davecgh/go-spew/spew"
)

type Example struct {
    Name string
    Age  int
}

func main() {
    example := &Example{
        Name: "Test",
        Age:  45,
    }

    // Using spew to dump the struct
    spew.Dump(example)
}

This would output something similar to:

(*main.Example)(0xc00000c420)({
 Name: (string) (len=4) "Test",
 Age: (int) 45
})

🧭 Related

πŸ”– Note

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/dgraph-io/ristretto

πŸ€–οΈ AI Intro

ristretto is a high performance memory-bound Go cache. It is centered around a proactive eviction policy that can outperform traditional LRU algorithms in many scenarios and workloads, which can be beneficial particularly in dealing with large volumes of data.

πŸ“ Example

package main

import (
    "fmt"

    "github.com/dgraph-io/ristretto"
)

func main() {
    cache, err := ristretto.NewCache(&ristretto.Config{
        NumCounters: 1e7,     // number of keys to track frequency of (10M).
        MaxCost:     1 << 30, // maximum cost of cache (1GB).
        BufferItems: 64,      // number of keys per Get buffer.
    })
    if err != nil {
        panic(err)
    }

    // set a value with a cost of 1
    cache.Set("key", "value", 1)

    // wait for value to pass through buffers
    cache.Wait()

    // get value from cache
    value, found := cache.Get("key")
    if !found {
        panic("missing value")
    }
    fmt.Println(value)

    // del value from cache
    cache.Del("key")
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/go-redsync/redsync/v4

πŸ€–οΈ AI Intro

redsync is a distributed mutual exclusion lock implementation in Go for Redis. It's based on the Redlock algorithm but has its own improvements for reliability, such as not relying on pub/sub for communication, which makes it a suitable component for distributed systems that require a high degree of synchronization.

πŸ“ Example

package main

import (
    goredislib "github.com/redis/go-redis/v9"
    "github.com/go-redsync/redsync/v4"
    "github.com/go-redsync/redsync/v4/redis/goredis/v9"
)

func main() {
    // Create a pool with go-redis (or redigo) which is the pool redisync will
    // use while communicating with Redis. This can also be any pool that
    // implements the `redis.Pool` interface.
    client := goredislib.NewClient(&goredislib.Options{
        Addr: "localhost:6379",
    })
    pool := goredis.NewPool(client) // or, pool := redigo.NewPool(...)

    // Create an instance of redisync to be used to obtain a mutual exclusion
    // lock.
    rs := redsync.New(pool)

    // Obtain a new mutex by using the same name for all instances wanting the
    // same lock.
    mutexname := "my-global-mutex"
    mutex := rs.NewMutex(mutexname)

    // Obtain a lock for our given mutex. After this is successful, no one else
    // can obtain the same lock (the same mutex name) until we unlock it.
    if err := mutex.Lock(); err != nil {
        panic(err)
    }

    // Do your work that requires the lock.

    // Release the lock so other processes or threads can obtain a lock.
    if ok, err := mutex.Unlock(); !ok || err != nil {
        panic("unlock failed")
    }
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

gorm.io/gorm

πŸ€–οΈ AI Intro

gorm is a powerful and easy-to-use Object-Relational Mapping (ORM) library for Go programming language (Golang). It automates database operations, reduces the amount of code to write, and makes your code more manageable. It fully supports struct-tag-based query, hooking before and after operations, combining complex transactions, query embedding, and much more. It facilitates database operation for Golang projects and makes it easier to maintain the complex data handling part of applications.

πŸ“ Example

package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

type Product struct {
  gorm.Model
  Code  string
  Price uint
}

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // Migrate the schema
  db.AutoMigrate(&Product{})

  // Create
  db.Create(&Product{Code: "D42", Price: 100})

  // Read
  var product Product
  db.First(&product, 1) // find product with integer primary key
  db.First(&product, "code = ?", "D42") // find product with code D42

  // Update - update product's price to 200
  db.Model(&product).Update("Price", 200)
  // Update - update multiple fields
  db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // non-zero fields
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

  // Delete - delete product
  db.Delete(&product, 1)
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

go.mongodb.org/mongo-driver

πŸ€–οΈ AI Intro

The official MongoDB driver for Go, it provides comprehensive functionality for working with MongoDB. Core features include sessions, transactions, and linearizable reads. With the MongoDB Go driver, you get a strongly typed, comprehensive API, built by MongoDB for Go developers.

πŸ“ Example

package main

import (
  "context"
  "fmt"

  "go.mongodb.org/mongo-driver/bson"
  "go.mongodb.org/mongo-driver/mongo"
  "go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
  // Set client options
  clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

  // Connect to MongoDB
  client, err := mongo.Connect(context.TODO(), clientOptions)

  if err != nil {
    log.Fatal(err)
  }

  // Check the connection
  err = client.Ping(context.TODO(), nil)

  if err != nil {
    log.Fatal(err)
  }

  fmt.Println("Connected to MongoDB!")

  collection := client.Database("test").Collection("trainers")

  trainer := bson.M{"id": "1", "name": "Ash", "age": 10, "city": "Pallet Town"}

  insertResult, err := collection.InsertOne(context.TODO(), trainer)

  if err != nil {
    log.Fatal(err)
  }

  fmt.Println("Inserted a single document: ", insertResult.InsertedID)
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/cloudflare/tableflip

πŸ€–οΈ AI Intro

tableflip enables developers to handle process restarts gracefully in Go. This package is vastly used in high-performance systems where it's crucial to maintain existing connections during a program's restart to ensure service continuity.

πŸ“ Example

upg, _ := tableflip.New(tableflip.Options{})
defer upg.Stop()

go func() {
    sig := make(chan os.Signal, 1)
    signal.Notify(sig, syscall.SIGHUP)
    for range sig {
        upg.Upgrade()
    }
}()

// Listen must be called before Ready
ln, _ := upg.Listen("tcp", "localhost:8080")
defer ln.Close()

go http.Serve(ln, nil)

if err := upg.Ready(); err != nil {
    panic(err)
}

<-upg.Exit()

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/hibiken/asynq

πŸ€–οΈ AI Intro

asynq is a Go library for queueing tasks and processing them asynchronously with workers. It's backed by Redis and is designed to be scalable yet user-friendly. This package offers a comprehensive set of features for managing distributed tasks, such as guaranteed execution, task scheduling, retries for failed tasks, automatic recovery of tasks in case of a crash, weighted and strict priority queues, task de-duplication, and more. It even supports Redis Cluster for auto-sharding and high availability, as well as integration with Prometheus for metrics collection and visualization.

πŸ“ Example

First, write a package that encapsulates task creation and task handling.

package tasks

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "time"

    "github.com/hibiken/asynq"
)

// A list of task types.
const (
    TypeEmailDelivery   = "email:deliver"
    TypeImageResize     = "image:resize"
)

type EmailDeliveryPayload struct {
    UserID     int
    TemplateID string
}

type ImageResizePayload struct {
    SourceURL string
}

//----------------------------------------------
// Write a function NewXXXTask to create a task.
// A task consists of a type and a payload.
//----------------------------------------------

func NewEmailDeliveryTask(userID int, tmplID string) (*asynq.Task, error) {
    payload, err := json.Marshal(EmailDeliveryPayload{UserID: userID, TemplateID: tmplID})
    if err != nil {
        return nil, err
    }
    return asynq.NewTask(TypeEmailDelivery, payload), nil
}

func NewImageResizeTask(src string) (*asynq.Task, error) {
    payload, err := json.Marshal(ImageResizePayload{SourceURL: src})
    if err != nil {
        return nil, err
    }
    // task options can be passed to NewTask, which can be overridden at enqueue time.
    return asynq.NewTask(TypeImageResize, payload, asynq.MaxRetry(5), asynq.Timeout(20 * time.Minute)), nil
}

//---------------------------------------------------------------
// Write a function HandleXXXTask to handle the input task.
// Note that it satisfies the asynq.HandlerFunc interface.
//
// Handler doesn't need to be a function. You can define a type
// that satisfies asynq.Handler interface. See examples below.
//---------------------------------------------------------------

func HandleEmailDeliveryTask(ctx context.Context, t *asynq.Task) error {
    var p EmailDeliveryPayload
    if err := json.Unmarshal(t.Payload(), &p); err != nil {
        return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
    }
    log.Printf("Sending Email to User: user_id=%d, template_id=%s", p.UserID, p.TemplateID)
    // Email delivery code ...
    return nil
}

// ImageProcessor implements asynq.Handler interface.
type ImageProcessor struct {
    // ... fields for struct
}

func (processor *ImageProcessor) ProcessTask(ctx context.Context, t *asynq.Task) error {
    var p ImageResizePayload
    if err := json.Unmarshal(t.Payload(), &p); err != nil {
        return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
    }
    log.Printf("Resizing image: src=%s", p.SourceURL)
    // Image resizing code ...
    return nil
}

func NewImageProcessor() *ImageProcessor {
    return &ImageProcessor{}
}

In your application code, import the above package and use Client to put tasks on queues.

package main

import (
    "log"
    "time"

    "github.com/hibiken/asynq"
    "your/app/package/tasks"
)

const redisAddr = "127.0.0.1:6379"

func main() {
    client := asynq.NewClient(asynq.RedisClientOpt{Addr: redisAddr})
    defer client.Close()

    // ------------------------------------------------------
    // Example 1: Enqueue task to be processed immediately.
    //            Use (*Client).Enqueue method.
    // ------------------------------------------------------

    task, err := tasks.NewEmailDeliveryTask(42, "some:template:id")
    if err != nil {
        log.Fatalf("could not create task: %v", err)
    }
    info, err := client.Enqueue(task)
    if err != nil {
        log.Fatalf("could not enqueue task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)

    // ------------------------------------------------------------
    // Example 2: Schedule task to be processed in the future.
    //            Use ProcessIn or ProcessAt option.
    // ------------------------------------------------------------

    info, err = client.Enqueue(task, asynq.ProcessIn(24*time.Hour))
    if err != nil {
        log.Fatalf("could not schedule task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)

    // ----------------------------------------------------------------------------
    // Example 3: Set other options to tune task processing behavior.
    //            Options include MaxRetry, Queue, Timeout, Deadline, Unique etc.
    // ----------------------------------------------------------------------------

    task, err = tasks.NewImageResizeTask("https://example.com/myassets/image.jpg")
    if err != nil {
        log.Fatalf("could not create task: %v", err)
    }
    info, err = client.Enqueue(task, asynq.MaxRetry(10), asynq.Timeout(3 * time.Minute))
    if err != nil {
        log.Fatalf("could not enqueue task: %v", err)
    }
    log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)
}

Next, start a worker server to process these tasks in the background. To start the background workers, use Server and provide your Handler to process the tasks.

You can optionally use ServeMux to create a handler, just as you would with net/http Handler.

package main

import (
    "log"

    "github.com/hibiken/asynq"
    "your/app/package/tasks"
)

const redisAddr = "127.0.0.1:6379"

func main() {
    srv := asynq.NewServer(
        asynq.RedisClientOpt{Addr: redisAddr},
        asynq.Config{
            // Specify how many concurrent workers to use
            Concurrency: 10,
            // Optionally specify multiple queues with different priority.
            Queues: map[string]int{
                "critical": 6,
                "default":  3,
                "low":      1,
            },
            // See the godoc for other configuration options
        },
    )

    // mux maps a type to a handler
    mux := asynq.NewServeMux()
    mux.HandleFunc(tasks.TypeEmailDelivery, tasks.HandleEmailDeliveryTask)
    mux.Handle(tasks.TypeImageResize, tasks.NewImageProcessor())
    // ...register other handlers...

    if err := srv.Run(mux); err != nil {
        log.Fatalf("could not run server: %v", err)
    }
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)
ryan961 commented 2 months ago

πŸ“¦ Package

github.com/spf13/cast

πŸ€–οΈ AI Intro

The github.com/spf13/cast package facilitates safe and easy type casting in Go. It offers a wide spectrum of intuitive functions for accomplishing tasks like turning a number into a string or an interface into a boolean. The beauty of this library lies in its explicitnessβ€”type conversion happens only when there's a clear path of compatibility between the types involved. It was created for use in Hugo, a static site generator that handles metadata in YAML, TOML, or JSON. It provides To_____E methods, which in addition to their respective To_____ methods, return an error on unsuccessful conversion.

πŸ“ Example

package main

import (
  "github.com/spf13/cast"
)

func main() {
  v := cast.ToString("mayonegg")         // "mayonegg"
  n := cast.ToInt("8")                  // 8
  c := cast.ToBool("true")              // true

  fmt.Println(v, n, c)
}

🧭 Related

[_**⏫️ Back to list**_](https://github.com/ryan961/memo/issues/2#issue-2080613557)