Skip to content

feat: add DST setup #879

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f66f8e2
test: add antithesis test
gfyrag Nov 12, 2024
df41f3d
chore: some test
gfyrag Nov 13, 2024
eead415
feat: first workable setup
gfyrag Nov 13, 2024
3b91230
Merge branch 'main' of github.com:formancehq/ledger into feat/dst
altitude Feb 21, 2025
24c347a
wip: bring up to date and new driver
altitude Feb 21, 2025
0addc57
fix: test composer changes
altitude Mar 13, 2025
dc15569
Merge branch 'main' of github.com:formancehq/ledger into feat/dst
altitude Mar 13, 2025
d64d1ef
wip: improve assertions
altitude Mar 13, 2025
32759cc
fix: overly ambitious assertions
altitude Mar 13, 2025
a380c0b
wip: add debugger
altitude Mar 13, 2025
b22e498
Merge remote-tracking branch 'origin/main' into feat/dst
altitude Apr 23, 2025
0919823
feat: add antithesis ci
altitude Apr 23, 2025
8aa8fb7
fix: antithesis registry docker login
altitude Apr 23, 2025
6d3747a
fix: antithesis docker login
altitude Apr 23, 2025
256bfb9
fix: antithesis github workflow
altitude Apr 23, 2025
10acc49
fix: workload build
altitude Apr 23, 2025
a2351d6
feat: improve test composing sequence
altitude May 2, 2025
58524d7
feat: improve test composer sequence
altitude May 5, 2025
4ed1eb5
fix: volumes driver
altitude May 5, 2025
8aaa393
fix: don't overuse assertions
altitude May 5, 2025
3ec2e4c
fix: ledger id
altitude May 5, 2025
9f22e06
fix: bucket/ledger
altitude May 5, 2025
aa9fa2b
Merge remote-tracking branch 'origin/main' into feat/dst
altitude May 5, 2025
e8b6e82
fix: remove exhaustive assertion on tx
altitude May 5, 2025
9fc1cf7
fix: switch to sometimes assertions for tx commit
altitude May 5, 2025
e52be33
feat: switch ledger creation to sometimes assertions as well
altitude May 5, 2025
40c1a39
fix: move correctness check to eventually
altitude May 5, 2025
c8ab708
feat: improve error handling on eventually
altitude May 6, 2025
f8fa1ba
Merge remote-tracking branch 'origin/main' into feat/dst
altitude May 6, 2025
979b33a
fix: improve error handling
altitude May 6, 2025
49b0414
wip: improve
altitude May 7, 2025
9248bd8
feat: add Kubernetes pod configuration for workload
flemzord Jun 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/antithesis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Antithesis Integration
on:
workflow_dispatch:
inputs:
run_tests:
description: 'Run Antithesis tests after pushing images'
type: boolean
default: true
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
paths:
- 'test/antithesis/**'

jobs:
push-antithesis-images:
name: Push Images to Antithesis
runs-on: shipfox-4vcpu-ubuntu-2404
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Earthly
uses: earthly/actions-setup@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
version: "latest"

- name: Setup Environment
uses: ./.github/actions/default
with:
token: ${{ secrets.NUMARY_GITHUB_TOKEN }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Antithesis Docker Registry
run: |
echo '${{ secrets.ANTITHESIS_JSON_KEY }}' | docker login -u _json_key https://us-central1-docker.pkg.dev --password-stdin

- name: Build and Push Config Image
run: |
cd test/antithesis
earthly --push +requirements-build --ANTITHESIS_REPOSITORY=${{ secrets.ANTITHESIS_REPOSITORY }}

- name: Run Antithesis Tests
if: ${{ github.event_name == 'workflow_dispatch' && inputs.run_tests == true }}
run: |
cd test/antithesis
earthly +run --ANTITHESIS_SLACK_REPORT_RECIPIENT="${{ secrets.ANTITHESIS_SLACK_REPORT_RECIPIENT }}" --ANTITHESIS_REPOSITORY=${{ secrets.ANTITHESIS_REGISTRY }} --ANTITHESIS_PASSWORD=${{ secrets.ANTITHESIS_PASSWORD }}
# env:
# ANTITHESIS_PASSWORD: ${{ secrets.ANTITHESIS_PASSWORD }}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ replace google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215 => google.
require (
github.com/ThreeDotsLabs/watermill v1.4.6
github.com/alitto/pond v1.9.2
github.com/antithesishq/antithesis-sdk-go v0.4.3
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10
github.com/bluele/gcache v0.0.2
github.com/dop251/goja v0.0.0-20241009100908-5f46f2705ca3
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alitto/pond v1.9.2 h1:9Qb75z/scEZVCoSU+osVmQ0I0JOeLfdTDafrbcJ8CLs=
github.com/alitto/pond v1.9.2/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI=
github.com/antithesishq/antithesis-sdk-go v0.4.3 h1:a2hGdDogClzHzFu20r1z0tzD6zwSWUipiaerAjZVP90=
github.com/antithesishq/antithesis-sdk-go v0.4.3/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
Expand Down
5 changes: 3 additions & 2 deletions internal/controller/system/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package system

import (
"context"
"reflect"
"time"

"github.com/formancehq/ledger/internal/storage/common"
"github.com/formancehq/ledger/pkg/features"
"go.opentelemetry.io/otel/attribute"
"reflect"
"time"

"go.opentelemetry.io/otel/metric"
noopmetrics "go.opentelemetry.io/otel/metric/noop"
Expand Down
43 changes: 40 additions & 3 deletions internal/storage/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
"context"
"errors"
"fmt"
"sync"
"time"

"github.com/antithesishq/antithesis-sdk-go/assert"
"github.com/formancehq/ledger/internal/storage/common"
systemstore "github.com/formancehq/ledger/internal/storage/system"
"github.com/formancehq/ledger/internal/tracing"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
"sync"
"time"

"github.com/alitto/pond"
"github.com/formancehq/go-libs/v3/bun/bunpaginate"
Expand All @@ -35,13 +37,31 @@
parallelBucketMigrations int
}

func (d *Driver) CreateLedger(ctx context.Context, l *ledger.Ledger) (*ledgerstore.Store, error) {
/*
CreateLedger creates a new ledger in the system and sets up all necessary database objects.

The function follows these steps:
1. Create a ledger record in the system store (_system.ledgers table)
2. Get the bucket (database schema) for this ledger
3. Check if the bucket is already initialized:
a. If initialized: Verify it's up to date and add ledger-specific objects to it
b. If not initialized: Create the bucket schema with all necessary tables
4. Return a ledger store that provides an interface to interact with the ledger

Note: This entire process is wrapped in a database transaction, ensuring atomicity.
If any step fails, the entire transaction is rolled back, preventing partial state.
*/
func (d *Driver) CreateLedger(ctx context.Context, l *ledger.Ledger) (*ledgerstore.Store, error) {
var ret *ledgerstore.Store

// Run the entire ledger creation process in a transaction for atomicity
// This ensures that either all steps succeed or none do (preventing partial state)
err := d.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
systemStore := d.systemStoreFactory.Create(d.db)

// Step 1: Create the ledger record in the system store
if err := systemStore.CreateLedger(ctx, l); err != nil {
// Handle the case where the ledger already exists
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know comments are useful, but this kind of comment just rephrase the code ^^

if errors.Is(postgres.ResolveError(err), postgres.ErrConstraintsFailed{}) {
return systemcontroller.ErrLedgerAlreadyExists
}
Expand All @@ -53,13 +73,26 @@
if err != nil {
return fmt.Errorf("checking if bucket is initialized: %w", err)
}

if isInitialized {
upToDate, err := b.IsUpToDate(ctx, tx)
if err != nil {
return fmt.Errorf("checking if bucket is up to date: %w", err)
}

if !upToDate {
assert.AlwaysOrUnreachable(
// @todo: replace this with a proper flag detailing wether we're
// operating a new version of the binary or not.
// if we are, we are definitely expecting this to happen.
// if we're not, this should be unreachable.
false,
"Bucket is outdated",
map[string]any{
"bucket": l.Bucket,
},
)

Check warning on line 95 in internal/storage/driver/driver.go

View check run for this annotation

Codecov / codecov/patch

internal/storage/driver/driver.go#L84-L95

Added lines #L84 - L95 were not covered by tests
Comment on lines +84 to +95
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Todo indicates temporary assertion pattern

The assertion is using false as the condition, which means it will always trigger as "unreachable". The TODO comments indicate this is a temporary solution, but this code path isn't covered by tests.

Consider:

  1. Adding a proper flag parameter as mentioned in the TODO
  2. Adding tests to cover this code path

🏁 Script executed:

#!/bin/bash
# Check if there are any tests covering this assertion in the driver code
rg -A 5 -B 5 "test.*Bucket.*Outdated" --type go

Length of output: 49


To be certain this assertion path is untested, let’s list any driver test files and search them for AlwaysOrUnreachable:


🏁 Script executed:

#!/bin/bash
# List Go test files in internal/storage/driver
fd -e go --glob '*_test.go' internal/storage/driver

# Search those test files for AlwaysOrUnreachable
rg "AlwaysOrUnreachable" -n --glob "internal/storage/driver/*_test.go"

Length of output: 348


Ensure proper flag control and add tests for the unreachable assertion

The assert.AlwaysOrUnreachable(false, …) at internal/storage/driver/driver.go:84–95 is built to always fire, and our search shows no tests reference AlwaysOrUnreachable to cover this path. To address:

• Introduce the boolean flag referenced in the TODO (e.g. isNewBinaryVersion) instead of hard‑coding false.
• Add unit tests in internal/storage/driver/driver_test.go (or a new test) that cover both cases:

  • Flag = true (assertion bypassed)
  • Flag = false (assertion triggers)

This will satisfy the TODO and ensure the code path is exercised.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 84-95: internal/storage/driver/driver.go#L84-L95
Added lines #L84 - L95 were not covered by tests

return systemcontroller.ErrBucketOutdated
}

Expand All @@ -72,14 +105,18 @@
}
}

// Step 4: Create a store for interacting with the ledger
ret = d.ledgerStoreFactory.Create(b, *l)

return nil
})

// If any error occurred during the transaction, resolve and return it
if err != nil {
return nil, postgres.ResolveError(err)
}

// Return the created ledger store
return ret, nil
}

Expand Down
8 changes: 8 additions & 0 deletions pkg/client/Earthfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
VERSION 0.7
Copy link
Contributor

@gfyrag gfyrag Apr 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop using Earthfile, there is no Earthfile anymore in the repsoitory ;)


FROM --platform=linux/amd64 golang:1.22.2

client:
FROM scratch
COPY . /client
SAVE ARTIFACT /client client
58 changes: 58 additions & 0 deletions test/antithesis/Earthfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
VERSION 0.8

IMPORT github.com/formancehq/earthly:tags/v0.17.1 AS core

FROM core+base-image

run:
WAIT
BUILD +requirements-build
END

FROM curlimages/curl
ARG ANTITHESIS_USERNAME=formance
ARG --required ANTITHESIS_SLACK_REPORT_RECIPIENT
RUN --no-cache --secret ANTITHESIS_PASSWORD curl \
--fail \
--user "$ANTITHESIS_USERNAME:$ANTITHESIS_PASSWORD" \
-X POST https://formance.antithesis.com/api/v1/launch_experiment/formance -d "{
\"params\": {
\"custom.duration\": \"0.1\",
\"antithesis.report.recipients\": \"${ANTITHESIS_SLACK_REPORT_RECIPIENT}\",
\"antithesis.config_image\": \"antithesis-config:latest\",
\"antithesis.images\": \"ledger:latest;workload:latest\"
}
}"

debugger:
FROM curlimages/curl

ARG ANTITHESIS_USERNAME=formance
ARG --required ANTITHESIS_SLACK_REPORT_RECIPIENT
ARG --required VTIME
ARG --required SESSION_ID
ARG --required HASH
RUN echo "{
\"params\": {
\"antithesis.debugging.session_id\": \"${SESSION_ID}\",
\"antithesis.debugging.input_hash\": \"${HASH}\",
\"antithesis.debugging.vtime\": \"${VTIME}\",
\"antithesis.report.recipients\": \"${ANTITHESIS_SLACK_REPORT_RECIPIENT}\"
}
}" > /tmp/debug_params.json

# Display the debug parameters for verification
RUN cat /tmp/debug_params.json

RUN --no-cache --secret ANTITHESIS_PASSWORD curl \
--fail \
--user "$ANTITHESIS_USERNAME:$ANTITHESIS_PASSWORD" \
-X POST https://formance.antithesis.com/api/v1/launch/debugging \
-d @/tmp/debug_params.json

requirements-build:
ARG --required ANTITHESIS_REPOSITORY

BUILD --pass-args ./config+build
BUILD --pass-args ./image+build
BUILD --pass-args ./workload+build
11 changes: 11 additions & 0 deletions test/antithesis/config/Earthfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
VERSION 0.8

IMPORT github.com/formancehq/earthly:tags/v0.17.1 AS core

build:
FROM --platform=linux/amd64 scratch
ARG --required ANTITHESIS_REPOSITORY
COPY docker-compose.yml /docker-compose.yml
COPY --dir gateway /gateway

SAVE IMAGE --push --no-manifest-list ${ANTITHESIS_REPOSITORY}/antithesis-config:latest
88 changes: 88 additions & 0 deletions test/antithesis/config/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
networks:
formance:
driver: bridge
ipam:
config:
- subnet: 10.0.29.0/24

services:
postgres:
image: "postgres:15-alpine"
hostname: postgres
container_name: postgres
command:
- -c
- max_connections=100
environment:
POSTGRES_USER: "ledger"
POSTGRES_PASSWORD: "ledger"
POSTGRES_DB: "ledger"
PGDATA: /data/postgres
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U ledger" ]
interval: 10s
timeout: 5s
retries: 5
networks:
formance:
ipv4_address: 10.0.29.16

ledger1:
image: "us-central1-docker.pkg.dev/molten-verve-216720/formance-repository/ledger:latest"
hostname: ledger1
container_name: ledger1
environment:
POSTGRES_URI: "postgresql://ledger:ledger@10.0.29.16:5432/ledger?sslmode=disable"
BIND: ":8080"
POSTGRES_CONN_MAX_IDLE_TIME: "1m0s"
POSTGRES_MAX_IDLE_CONNS: "20"
POSTGRES_MAX_OPEN_CONNS: "20"
networks:
formance:
ipv4_address: 10.0.29.17
depends_on:
postgres:
condition: service_healthy

ledger2:
image: "us-central1-docker.pkg.dev/molten-verve-216720/formance-repository/ledger:latest"
hostname: ledger2
container_name: ledger2
environment:
POSTGRES_URI: "postgresql://ledger:ledger@10.0.29.16:5432/ledger?sslmode=disable"
POSTGRES_CONN_MAX_IDLE_TIME: "1m0s"
POSTGRES_MAX_IDLE_CONNS: "20"
POSTGRES_MAX_OPEN_CONNS: "20"
BIND: ":8080"
networks:
formance:
ipv4_address: 10.0.29.18
depends_on:
postgres:
condition: service_healthy

workload:
image: "us-central1-docker.pkg.dev/molten-verve-216720/formance-repository/workload:latest"
hostname: workload
container_name: workload
networks:
formance:
ipv4_address: 10.0.29.19
depends_on:
- gateway

gateway:
image: "ghcr.io/formancehq/gateway:v2.0.0-rc.20"
hostname: gateway
container_name: gateway
volumes:
- ./gateway/Caddyfile:/etc/caddy/Caddyfile
ports:
- "8080:8080"
networks:
formance:
ipv4_address: 10.0.29.20
depends_on:
- ledger1
- ledger2
12 changes: 12 additions & 0 deletions test/antithesis/config/gateway/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
# Local env dev config
auto_https off
debug
}

:8080 {
reverse_proxy {
to ledger1:8080 ledger2:8080
lb_policy first
}
}
Loading
Loading