Create a Ledger Utilizing CockroachDB - Part I - Introduction
Building an accounting ledger with CockroachDB - Introduction
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.
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
Java 17
Maven 3+
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.