Create a Ledger Utilizing CockroachDB - Part I - Introduction

Create a Ledger Utilizing CockroachDB - Part I - Introduction

Building an accounting ledger with CockroachDB - Introduction

·

5 min read

This is the first part of a series about RoachBank, a full-stack financial accounting ledger demo running on both CockroachDB and PostgreSQL. It's designed to demonstrate the safety and liveness properties of a globally deployed, system-of-record type of financial workload.

Introduction

The concept behind the ledger is to move funds between monetary accounts using balanced, multi-legged transactions, at a high frequency. As a financial system, correctness is defined as conserving money at all times and providing an audit trail of monetary transactions performed towards the accounts. Put simply, when externally observing the system, the total account balance must be constant at all times. Funds are simply moved between different accounts using balanced transactions.

This is visualized by the service using a single page to display accounts as rectangles with their current balance.

Key Invariants

There are a few business rule invariants that must hold at all times regardless of observer and activities. Such as infrastructure failure (nodes crashing) or conflicting operations when concurrently updating the same accounts.

  • The total balance of all accounts must be constant.

  • User accounts must have a positive balance (account types that disallow negative balance).

  • An audit trail of all transactions must be stored from which the account balances can be derived.

The system must refuse forward progress if an operation would result in any of these invariants being compromised. For example, if a variation of the total balances is observed at any given time, then money has either been "invented" or "destroyed".

These invariants are safeguarded by ACID guarantees and real serializable transactions. CockroachDB defaults to only serializable while PostgreSQL defaults to read-committed but can be elevated to serializable-snapshot or SSI.

Double-entry Bookkeeping

To satisfy the audit trail requirement, the ledger follows the double-entry bookkeeping principle. This principle was originally formalized and published by the Italian mathematician Luca Pacioli during the 15th century.

Portrait of Luca Pacioli

It involves making at least two account entries for every transaction. A debit in one account and a corresponding credit in another account. The sum of all debits must equal the sum of all credits, providing a simple method for error detection. Real accounting doesn't use negative numbers, but for simplicity, this ledger does (it's not about modelling the true complexity of accounting).

A positive value means increasing the value (credit), and a negative value means decreasing the value (debit). A transaction is considered balanced when the sum of the legs with the same currency equals zero.

In the following example, there are four different accounts involved with zero-sum in the end.

Account | Credit(+) | Debit(-) |
A         100               
B                     -50
C          25
D*                    -25 \
                           -75 (coalesced)
D*                    -50 /
------------------------------------------
Σ         125    +   -125 = 0

Building

Prerequisites

The service is built with Maven 3.1+. Tanuki's Maven wrapper is included (mvnw) so Maven is optional. All 3rd party dependencies are available in public Maven repositories except for the CockroachDB JDBC driver which is available in GitHub Packages (you only need a GitHub account).

These dependencies are available in GitHub packages:

<dependency>
    <groupId>io.cockroachdb.jdbc</groupId>
    <artifactId>cockroachdb-jdbc-driver</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>io.cockroachdb</groupId>
    <artifactId>spring-data-cockroachdb</artifactId>
    <version>1.0.0</version>
</dependency>

To allow Maven to use this repository, add a github profile (or similar) to your Maven settings.xml file. Edit $user.dir/.m2/settings.xml.

An example is provided below:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
  <servers>
    <server>
        <id>github</id>
        <username>your-github-id</username>
        <password>your-personal-access-token</password>
    </server>
  </servers>

  <mirrors>
    <!-- default setting -->
    <mirror>
      <id>maven-default-http-blocker</id>
      <mirrorOf>external:http:*</mirrorOf>
      <name>Pseudo repository to mirror external repositories initially using HTTP.</name>
      <url>http://0.0.0.0/</url>
      <blocked>true</blocked>
    </mirror>
  </mirrors>

  <profiles>
      <profile>
        <id>github</id>
        <repositories>
            <repository>
                <id>central</id>
                <url>https://repo1.maven.org/maven2</url>
            </repository>
            <repository>
                <id>github</id>
                <url>https://maven.pkg.github.com/cockroachlabs-field/*</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
                <releases>
                    <enabled>true</enabled>
                </releases>
            </repository>
        </repositories>
      </profile>
  </profiles>

  <activeProfiles>
    <activeProfile>github</activeProfile>
  </activeProfiles>
</settings>

Clone the project

git clone git@github.com:kai-niemi/roach-bank.git

Build the executable jars

Using installed Maven:

cd roach-bank 
chmod +x mvnw 
mvn clean install

Using the Maven wrapper (where you need to specify settings.xml):

cd roach-bank 
chmod +x mvnw 
./mvnw clean install -s <path-to>/settings.xml

Local Deployment

Assuming you already have a local CockrochDB cluster running.

First, create the database:

cockroach sql --insecure --host=localhost -e "CREATE database roach_bank"

Then start the server:

java -jar bank-server/target/bank-server.jar

Then start the client:

java -jar bank-client/target/bank-client.jar

The client is used to issue business transactions to the server's REST API. The client and server could be on separate hosts with an L7 load balancer in-between, but for convenience, the client connects to localhost by default.

Next Steps

In the second part of this series, we'll cover how to run the bank against a multi-regional cloud deployment. The third part goes into design details and the technology stack.

Conclusion

RoachBank is a full-stack financial accounting ledger demo running on both CockroachDB and PostgreSQL. It follows the double-entry bookkeeping principle and is designed to demonstrate the safety and liveness properties of a globally deployed, system-of-record type of financial workload. This article provides instructions on how to set up and run the demo, as well as details on the technology stack and design.

Did you find this article valuable?

Support Kai Niemi by becoming a sponsor. Any amount is appreciated!