Posts

Opentelemetry - Log-Based Change Data Capture tracing

Opentelemetry provides vendor neutral standards and implementations to generate, collect, and export tracing data.

A Context is a propagation mechanism which carries execution-scoped values across API boundaries and between logically associated execution units. Cross-cutting concerns access their data in-process using the same shared Context object.

Context propagation is required when the tracing needs to cross process or service boundaries. Common ways to propagate the context is using W3C Trace Context or Zipkin B3 headers, while to inject the context is, for example, http headers or metadata fields in event messages.

Go Generic Programming - Testing the waters

Go Type Parameters Proposal is expected to be implemented with Go 1.18 early 2022.

The Very high level overview section contains a nice overview of what to expect.

If you have seen generics in other languages perhaps the most curious differences (other than using square brackets), are union types:

type SignedIntegers interface {
	int | int8 | int16 | int32 | int64
}

Similar in syntax to scala3/dotty union types, although in Go is only allowed in constraints. The type set of this union element is the set {int, int8, int16, int32, int64}.

Go - On logging and dependencies

Doing code reviews, I have sometimes noticed libraries forcing dependencies on the users.

While it is perfectly acceptable if well justified and documented, it sometimes feels like adding unnecessary baggage.

A typical example would be something like the following snippet:

func (c *Client) HealthCheck(ctx context.Context) error {
    logrus.Debug("sending get request")
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url, nil)
    if err != nil {
        return err
    }

    resp, err := c.client.Do(req)
    if err != nil {
        return err
    }

    logrus.Debugf("http status code was %d", resp.StatusCode)

    defer resp.Body.Close()

    return nil
}

Using logrus, as example, picking this code, one would need to check how logrus works, which configurations are available, which version should be used and so on.

PostgreSQL - UUID vs TEXT

In PostgreSQL one can represent UUID as UUID, TEXT or VARCHAR built-in types.

Which type represents a UUID more efficiently? Mainly in the JSONB context?

My gut feeling says built-in type UUID is way better.

One trap with using TEXT is trying to compare different cases with equals:

select 'ef9f94da-98ef-49fa-8224-32f3e1f592b3' = 'EF9F94DA-98EF-49FA-8224-32F3E1F592B3' as equal;
 equal
-------
 f

Checking the relevant RFC4122 section:

Each field is treated as an integer and has its value printed as a
zero-filled hexadecimal digit string with the most significant
digit first.  The hexadecimal values "a" through "f" are output as
lower case characters and are case insensitive on input.

Which the UUID type correctly handles:

Go - Embed

Go 1.16 added a new embed package.

While the idea is not a novelty, there were already some packages with slightly different APIs fulfilling similar roles, for example:

Having an official package is always a welcome addition to the batteries-included standard library.

Usage

A possible way to embed react assets could be as simple as:

package web

import (
    "embed"
    "io/fs"
)

//go:embed build/*
var content embed.FS

And serving assets using http.FileServer:

Go - On building URL strings

Building URL strings in Go may be accomplished in a couple different ways:

  • url.Parse
  • string concatenation (using +)
  • fmt.Sprintf
  • bytes.Buffer
  • strings.Builder

You may be asking yourself: which one should I use, then? As always, the answer depends. Let us explore why.

URL.Parse

Before we start, a quick refresh on the URL structure:

url

One important fact to be aware is that when building URLs manually, it is easy to forget the percentage-encoding.

Writing a client library in Go

There are multiple ways of writing client libraries in Go. In this post, I will explore what I look for in a library depending if I am developing or checking if it is suitable for a project.

Without any specific order of importance, these are my thoughts on the subject.

Usage and Examples

Like a book can be judged by its cover, a project can be judged by its README. Therefore providing examples helps getting an idea of how the library works: if it will fit a project or how good the developer usability is (this includes function naming, usage difficulty or how easy it is to misuse).

A story about Go http.Client

Or how I have learned to embrace http.RoundTripper

Using a http client in Go usually starts like this:

resp, err := http.Get("http://example.com/")

Everything works, until it doesn’t: a network blip, a connection reset, a slow
response, etc.

After some research (I recommend reading The complete guide to Go net/http
timeouts
)
you may end up writing something similar to:

c := &http.Client{
    Transport: &http.Transport{
        Dial: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).Dial,
        TLSHandshakeTimeout:   10 * time.Second,
        ResponseHeaderTimeout: 10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    },
}

req, _:= http.NewRequest(http.MethodGet, "https://www.google.com", nil)

resp, err := c.Do(req)

Ok! But now the question is: how does http.Client really work?

Kafka — Accelerating!

Quick tips and insights on how to make Apache Kafka
work faster!

Hardware

  • CPU doesn’t matter that much.
  • Memory helps a lot (a lot) in performance.
  • SSDs are not required, since most operations are sequential read and writes.
  • If possible run in bare
    metal
    .

Linux

vm.dirty_background_ratio = 5
vm.dirty_ratio = 80
vm.swappiness = 1
  • Assuming you are using ext4, don’t waste space with reserved blocks:
tune2fs -m 0 -i 0 -c -1 /dev/device
  • Mount with noatime:
/dev/device       /mountpoint       ext4    defaults,noatime
  • Keep an eye on the number of free inodes:
tune2fs -l /dev/device | grep -i inode
  • Increase limits, for example, using systemd:
$ cat /etc/systemd/system/kafka.service.d/limits.conf
[Service]
LimitNOFILE=10000
  • Tweak your network settings, for example:
net.core.somaxconn = 1024
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.ipv4.tcp_rmem = 4096 87380 33554432
net.ipv4.tcp_wmem = 4096 65536 33554432
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_syncookies = 1

Kafka

  • log.dirs accepts a comma separated list of disks and will distribute
    partitions across them, however:
  1. Doesn’t rebalance, some disks could be full and others empty.
  2. Doesn’t tolerate any disk failure, more info in
    KIP-18.
  3. Raid 10 is probably the best middle ground between performance and
    reliability.
  • *num.io.threads, *number of I/O threads that the server uses for executing
    requests. You should have at least as many threads as you have disks.
  • num.network.threads, number of network threads that the server uses for
    handling network requests. Increase based on number of producers/consumers and
    replication factor.
  • Use Java 1.8 and G1 Garbage
    collector
    :
-XX:MetaspaceSize=96m
-XX:+UseG1GC            # use G1
-XX:MaxGCPauseMillis=20 # gc deadline
-XX:InitiatingHeapOccupancyPercent=35
-XX:G1HeapRegionSize=16M
-XX:MinMetaspaceFreeRatio=50 -XX:MaxMetaspaceFreeRatio=80
  • KAFKA_HEAP_OPTS, 5–8Gb heap should be enough for most deployments, file system
    cache is way more important. Linkedin runs 5Gb heap in 32Gb RAM
    servers
    .
  • pcstat can help understand how well the
    system is caching:
./pcstat /kafka/data/*

Any comments or suggestions are welcome!

Stability Patterns — Circuit Breaker

A circuit breaker is an automatically operated electrical switch designed to
protect an electrical circuit from damage caused by over-current or overload or
short circuit. Its basic function is to interrupt current flow after protective
relays detect a fault. A circuit breaker can be reset (either manually or
automatically) to resume normal operation.

The software analogue as described in Release
it!
chapter 5.2 can prevent repeated
calls to a failing service by detecting issues and providing a fallback, by
using this pattern it is possible to avoid cascading failures.