Testing TigerBeetle Applications Using Testcontainers

Discover TigerBeetle, the high-performance, distributed financial accounting database optimized for mission-critical safety and consistency. Learn how it simplifies financial transaction services and explore a new Testcontainers module for TigerBeetle in Golang.

There are a lot of T's up in the title. Who knows, maybe it is Testing Tuesday?

What is TigerBeetle?

TigerBeetle is a distributed financial accounting database designed for mission critical safety and performance. - TigerBeetle Docs

TLDR; it is a database specifically designed for financial transactions.

If you've ever worked on a financial application, you know it is hard to keep your service performant and consistent. The most common approach is to use a relational database such as PostgreSQL or MySQL.

Although this is OK, and many companies still use this approach to store their financial data, it is a cumbersome task for developers to manage these systems.

A few months ago, when I started to work on a service that allows users to transact with each other within Getcontact I thought this wouldn't be really hard. I was wrong, to put it mildly.

A financial transaction service should be aware of a lot of concepts:

  • Enforcing account balance limits,
  • Idempotency for clients to retry a transaction,
  • Currency conversion,
  • Amount precision,
  • Provision ( pending transfers ),
  • Refunds
  • Audit trail,

And the list goes on.

Where TigerBeetle helps most is that you have a database that has very strong consistency guarantees, is failure-tolerant, and is highly performant on your side. It is like you have Mike Tyson as your uncle, and you don't need to worry about someone attacking you, except Jake Paul. It takes a lot of cognitive load on your brain, and you can work on business-related functionality while your database stays performant and consistent.

It doesn't have a concept of refund, audit trail, or even currency. It has only two simple constructs that interact with each other:

  • Account
  • Transfer

From these two constructs, you can run Doom on your terminal! No, no... I was joking, but if you try out Tigerlings, you can see the capabilities of TigerBeetle yourselves.


Anyway, I've been experimenting with TigerBeetle, and before going out to production I am running some tests, as opposed to CrowdStrike. I am using Testcontainers for integration tests and noticed there isn't a module for TigerBeetle. I've seen it as an opportunity to contribute to a well-known repository. Just so you know, I wasn't one of those guys who flooded the ExpressJS repository with Update Readme PRs.

So, I've created a Testcontainers module for TigerBeetle, and happy to announce it here.

💡
If you would like to read more articles like this, hit that subscribe button, I will email you as soon as I fix my email server, so you don't need to worry about getting spammed for about a year or two!

Okay, I see some people still keep reading, and I know who they are. They are the developers who can't just quit an article without seeing some code. Otherwise, they are not satisfied. Here is how you can use the Testcontainers module for TigerBeetle in Golang:

Installation:

go get -u github.com/mkadirtan/testcontainers-tigerbeetle-go@latest

Installation command for testcontainers-tigerbeetle-go package for Golang, in Bash

package tigerbeetle_test

import (
	"context"
	"testing"

	tigerbeetle "github.com/mkadirtan/testcontainers-tigerbeetle-go"
	"github.com/stretchr/testify/require"
	tigerbeetle_go "github.com/tigerbeetle/tigerbeetle-go"
	"github.com/tigerbeetle/tigerbeetle-go/pkg/types"
)

func TestTigerbeetleContainer(t *testing.T) {
	ctx := context.Background()

	tbContainer, err := tigerbeetle.Run(ctx, tigerbeetle.DefaultImage)
	require.NoError(t, err)
	defer func() { _ = tbContainer.Terminate(ctx) }()

	address, err := tbContainer.Address(ctx)
	require.NoError(t, err)

	tbClient, err := tigerbeetle_go.NewClient(types.ToUint128(0), []string{address})
	require.NoError(t, err)

	_, err = tbClient.CreateAccounts([]types.Account{
		{
			ID:     types.ID(),
			Ledger: 1,
			Code:   1,
		},
	})
	require.NoError(t, err)
}

Example usage of TigerBeetle Testcontainers module in GoLang