Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
cc33893
feat(ledger): replace Pebble storage with RocksDB
flemzord Sep 29, 2026
f46a460
build(ledger): package static RocksDB releases and fix CI
flemzord Sep 29, 2026
746e540
feat(ledger): drop pre-release Pebble migration path
flemzord Sep 29, 2026
2f2b799
docs(ledger): align operations and dashboards with RocksDB
flemzord Sep 29, 2026
274dbb2
refactor(ledger): remove stale Pebble storage names
flemzord Sep 29, 2026
fa3f959
fix(ledger): include RocksDB in Antithesis image
flemzord Sep 29, 2026
726d99c
fix(ledger): link RocksDB in Antithesis workload
flemzord Sep 29, 2026
aaaf4ec
fix(antithesis): package MinIO for Kubernetes runs
flemzord Sep 29, 2026
7d27bf4
fix(antithesis): apply RocksDB cluster tuning
flemzord Sep 29, 2026
93e027d
fix(storage): link RocksDB compression codecs
flemzord Sep 29, 2026
4acb852
fix(storage): publish RocksDB checkpoints safely
flemzord Sep 29, 2026
10d2bab
style(storage): satisfy pre-commit formatting
flemzord Sep 29, 2026
68ef9e9
fix(storage): publish offline bootstrap checkpoint
flemzord Sep 29, 2026
213516a
fix(storage): isolate mutable RocksDB checkpoint files
flemzord Sep 29, 2026
b33ccd1
test(storage): cover independent RocksDB checkpoint clones
flemzord Sep 29, 2026
ecfbc3a
style(storage): match CI formatting
flemzord Sep 29, 2026
91c96b9
ci(ledger): retain model failure diagnostics
flemzord Sep 29, 2026
da40183
refactor(storage): remove stale Pebble reader names
flemzord Sep 29, 2026
5da7dde
test(storage): compare Pebble and RocksDB performance
flemzord Sep 29, 2026
3835c02
test(cluster): bound follower read retry calls
flemzord Sep 29, 2026
bd33bfa
fix(storage): parse custom benchmark metrics
flemzord Sep 29, 2026
157d630
test(storage): compare HTTP performance across engines
flemzord Sep 29, 2026
3487d74
fix(storage): use current cluster health endpoint
flemzord Sep 29, 2026
61f8b5b
test(storage): compare higher HTTP load
flemzord Sep 29, 2026
7c2143c
test(storage): use comparable intermediate load
flemzord Sep 29, 2026
e0bc628
docs(storage): record Pebble and RocksDB benchmark results
flemzord Sep 29, 2026
7211502
fix(ledgerctl): show RocksDB storage labels and supported metrics
flemzord Sep 30, 2026
5893c2c
test(model): diagnose prepared log page mismatches
flemzord Sep 30, 2026
f554b70
feat(storage): replace Pebble metrics contract with RocksDB properties
flemzord Sep 30, 2026
82c448e
docs(storage): record latest RocksDB latency variation
flemzord Sep 30, 2026
01248b2
refactor(storage): remove stale Pebble identifiers and diagnostics
flemzord Sep 30, 2026
ebcdb78
test(storage): align assertions with RocksDB diagnostics
flemzord Sep 30, 2026
59788cf
docs(storage): update stale Pebble read-path comments
flemzord Sep 30, 2026
fb9fdb7
fix(events): isolate webhook HTTP transport per sink
flemzord Sep 30, 2026
32cbda5
docs(storage): record repeat RocksDB performance measurements
flemzord Sep 30, 2026
8b1413b
refactor(storage): remove remaining Pebble key identifiers
flemzord Sep 30, 2026
fc4818c
test(antithesis): expose prepared log volume differences
flemzord Sep 30, 2026
13db004
fix(ledger): retain independent Raft removal barriers
flemzord Sep 30, 2026
20832b0
style(ledger): format removal barrier helpers
flemzord Sep 30, 2026
0a336a6
fix(test): model purged persisted zero volumes
flemzord Sep 30, 2026
b6b6640
docs(ledger): record current RocksDB performance comparison
flemzord Sep 30, 2026
769d0fe
fix(ledger): retry transient learner join cancellation
flemzord Sep 30, 2026
d8d8b53
style(ledger): format learner retry test
flemzord Sep 30, 2026
38ff04d
style(ledger): finish learner retry test formatting
flemzord Sep 30, 2026
23cceb4
fix(test): clear maintenance before stats source barrier
flemzord Sep 30, 2026
b7bd480
docs(ledger): record latest RocksDB performance results
flemzord Sep 30, 2026
e6d8f01
docs(ledger): document repeat storage benchmark variability
flemzord Sep 30, 2026
10a947a
feat(operator): isolate RocksDB control plane from Pebble
flemzord Sep 30, 2026
290a44a
fix(ledger): integrate preload validation before ACME qualification
flemzord Sep 30, 2026
1bbddb9
fix(storage): integrate cluster-local state isolation for RocksDB
flemzord Sep 30, 2026
d6af392
fix(ci): preserve RocksDB qualification across workload lint updates
flemzord Sep 30, 2026
4ccd3b4
fix(ci): normalize imports and spacing after upstream merges
flemzord Sep 30, 2026
dc9f911
feat(ci): qualify pinned RocksDB candidates with current audit state
flemzord Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ docs/
*.md
.claude/
.github/
.release-prebuilt/
46 changes: 34 additions & 12 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
name: Default
on:
workflow_dispatch:
inputs:
pr_number:
description: PR number used for SHA-pinned candidate image tags
required: true
type: number
merge_group:
push:
branches:
Expand Down Expand Up @@ -201,8 +207,21 @@ jobs:
- name: Setup Env
uses: ./.github/actions/default
- name: Run in-memory model checker (3-node cluster, rolling restarts)
env:
KEEP_WORKDIR: "1"
run: >
nix develop --command just test-model-cluster 180
- name: Upload model failure diagnostics
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: model-failure-diagnostics
path: |
${{ runner.temp }}/nix-shell.*/model-test.*/driver.log
${{ runner.temp }}/nix-shell.*/model-test.*/server-*.log
${{ runner.temp }}/nix-shell.*/model-test.*/assertions.json
retention-days: 3
if-no-files-found: warn

Coverage:
if: always()
Expand Down Expand Up @@ -270,13 +289,15 @@ jobs:
dissociate: true
- name: Setup Env
uses: ./.github/actions/default
- run: >
nix develop --command go build ./...
- name: Cross-compile ledgerctl for Windows
run: >
nix develop --command bash -c "mkdir -p build &&
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o build/ledgerctl-windows-amd64.exe ./cmd/ledgerctl &&
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o build/ledgerctl-windows-arm64.exe ./cmd/ledgerctl"
- name: Build all packages
run: nix develop --command just build-packages
- name: Build Linux server
run: nix develop --command just build
- name: Build Linux CLI
run: nix develop --command just build-client
# Windows cross-builds require a Windows C/C++ toolchain and RocksDB 11.1+
# headers/libraries for each target architecture; CGO_ENABLED=0 cannot
# compile grocksdb. Restore this target only with those prerequisites.
- name: Build operator
run: >
nix develop --command bash -c "cd misc/operator && go build ./..."
Expand Down Expand Up @@ -362,8 +383,9 @@ jobs:
# reviewer can deploy the PR build for smoke tests before merge.
# The label is opt-in (apply manually on the PR) — every push to a
# labeled PR republishes the image under a SHA-pinned tag, so no two
# PRs ever fight for the same tag.
if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'build-images')
# PRs ever fight for the same tag. Manual qualification uses the selected
# branch SHA and the same mandatory test gates, even while the PR base moves.
if: (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'build-images')) || github.event_name == 'workflow_dispatch'
needs:
[
Dirty,
Expand Down Expand Up @@ -401,7 +423,7 @@ jobs:
# Default checkout on `pull_request` produces a synthetic merge
# commit. Pin to the PR HEAD so the image tag matches the
# commit the contributor actually pushed.
ref: ${{ github.event.pull_request.head.sha }}
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup Env
uses: ./.github/actions/default
- name: Set up Docker Buildx
Expand All @@ -415,8 +437,8 @@ jobs:
- name: Compute image tag
id: tag
run: |
short=$(echo "${{ github.event.pull_request.head.sha }}" | cut -c1-7)
tag="pr-${{ github.event.pull_request.number }}-${short}"
short=$(echo "${{ github.event.pull_request.head.sha || github.sha }}" | cut -c1-7)
tag="pr-${{ github.event.pull_request.number || inputs.pr_number }}-${short}"
image="${IMAGE_REPOSITORY}:${tag}"
operator_image="${OPERATOR_IMAGE_REPOSITORY}:${tag}"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
Expand Down
99 changes: 99 additions & 0 deletions .github/workflows/storage-performance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Storage Performance Comparison

on:
workflow_dispatch:
pull_request:
paths:
- .github/workflows/storage-performance.yml
- scripts/compare-storage-performance.sh
- scripts/compare-storage-performance.py
- scripts/compare-storage-api.py
- scripts/storage-performance-api.js

permissions:
contents: read

jobs:
compare:
runs-on: namespace-profile-linux-amd64-4vcpu
env:
GOPATH: /tmp/go
PEBBLE_REF: c6a99fc92f43f5127c898fd9a727adee3424310d
ROCKSDB_REF: ${{ github.event.pull_request.head.sha || github.sha }}
PERF_OUTPUT_DIR: build/storage-performance
steps:
- uses: namespacelabs/nscloud-checkout-action@445c25d7009680597d73eb03c4e1cd5be522ed73 # v9
with:
fetch-depth: 0
dissociate: true
- name: Setup Env
uses: ./.github/actions/default
- name: Compare Pebble and RocksDB
run: nix develop --command bash scripts/compare-storage-performance.sh
- name: Upload benchmark samples
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: storage-performance
path: build/storage-performance/

api:
runs-on: namespace-profile-linux-amd64-4vcpu
env:
GOPATH: /tmp/go
PEBBLE_REF: c6a99fc92f43f5127c898fd9a727adee3424310d
ROCKSDB_REF: ${{ github.event.pull_request.head.sha || github.sha }}
steps:
- uses: namespacelabs/nscloud-checkout-action@445c25d7009680597d73eb03c4e1cd5be522ed73 # v9
with:
fetch-depth: 0
dissociate: true
- name: Setup Env
uses: ./.github/actions/default
- name: Compare HTTP write and read performance
run: nix develop --command python3 scripts/compare-storage-api.py
- name: Upload API benchmark samples
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: storage-api-performance
path: |
build/storage-api-performance/refs.json
build/storage-api-performance/results.json
build/storage-api-performance/*/warmup.json
build/storage-api-performance/*/measured.json
build/storage-api-performance/*/warmup.log
build/storage-api-performance/*/measured.log
build/storage-api-performance/*/server.log

api-pressure:
runs-on: namespace-profile-linux-amd64-4vcpu
env:
GOPATH: /tmp/go
PEBBLE_REF: c6a99fc92f43f5127c898fd9a727adee3424310d
ROCKSDB_REF: ${{ github.event.pull_request.head.sha || github.sha }}
PERF_OUTPUT_DIR: build/storage-api-pressure
WRITE_RATE: 50
READ_RATE: 25
steps:
- uses: namespacelabs/nscloud-checkout-action@445c25d7009680597d73eb03c4e1cd5be522ed73 # v9
with:
fetch-depth: 0
dissociate: true
- name: Setup Env
uses: ./.github/actions/default
- name: Compare HTTP performance at higher offered load
run: nix develop --command python3 scripts/compare-storage-api.py
- name: Upload higher-load API benchmark samples
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: storage-api-pressure
path: |
build/storage-api-pressure/refs.json
build/storage-api-pressure/results.json
build/storage-api-pressure/*/warmup.json
build/storage-api-pressure/*/measured.json
build/storage-api-pressure/*/warmup.log
build/storage-api-pressure/*/measured.log
build/storage-api-pressure/*/server.log
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ cmd/bench/cpu.prof

# Build directory (generated files, benchmark results, etc.)
build/
.release-prebuilt/
dist/

# K6 outputs
summary.json
Expand Down
55 changes: 7 additions & 48 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,56 +12,22 @@ env:
builds:
- binary: ledger-server
id: ledger-server
tags:
- kafka
- nats
- clickhouse
- databricks
- s3
- pyroscope
ldflags:
- -X github.com/formancehq/ledger/v3/internal/pkg/version.Version={{ .Version }}
- -X github.com/formancehq/ledger/v3/internal/pkg/version.Commit={{ .ShortCommit }}
- -X github.com/formancehq/ledger/v3/internal/pkg/version.BuildDate={{ .Date }}
- -extldflags=-static
env:
- CGO_ENABLED=0
builder: prebuilt
prebuilt:
path: .release-prebuilt/{{ .Os }}_{{ .Arch }}/ledger-server
goos:
- linux
- darwin
goarch:
- amd64
- arm64

- binary: ledgerctl
id: ledgerctl
main: ./cmd/ledgerctl
ldflags:
- -X github.com/formancehq/ledger/v3/internal/pkg/version.Version={{ .Version }}
- -X github.com/formancehq/ledger/v3/internal/pkg/version.Commit={{ .ShortCommit }}
- -X github.com/formancehq/ledger/v3/internal/pkg/version.BuildDate={{ .Date }}
- -extldflags=-static
env:
- CGO_ENABLED=0
builder: prebuilt
prebuilt:
path: .release-prebuilt/{{ .Os }}_{{ .Arch }}/ledgerctl
goos:
- linux
- darwin
goarch:
- amd64
- arm64

- binary: ledgerctl
id: ledgerctl-windows
main: ./cmd/ledgerctl
ldflags:
- -X github.com/formancehq/ledger/v3/internal/pkg/version.Version={{ .Version }}
- -X github.com/formancehq/ledger/v3/internal/pkg/version.Commit={{ .ShortCommit }}
- -X github.com/formancehq/ledger/v3/internal/pkg/version.BuildDate={{ .Date }}
- -extldflags=-static
env:
- CGO_ENABLED=0
goos:
- windows
goarch:
- amd64
- arm64
Expand All @@ -77,21 +43,14 @@ release:
- glob: openapi.yml

archives:
- id: ledger-unix
- id: ledger-linux
ids:
- ledger-server
- ledgerctl
formats:
- tar.gz
name_template: "{{ .ProjectName }}_{{ .Os }}-{{ .Arch }}"
wrap_in_directory: true
- id: ledger-windows
ids:
- ledgerctl-windows
formats:
- zip
name_template: "{{ .ProjectName }}_{{ .Os }}-{{ .Arch }}"
wrap_in_directory: true

checksum:
name_template: "checksums.txt"
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ These are guardrails, not complete explanations. Before changing the affected su

1. **Cache consistency:** the in-memory cache must not diverge between nodes for the same applied index.
2. **Deterministic FSM:** Raft apply must produce identical results on every node. No randomness, wall-clock-dependent behavior, or node-local state in the FSM apply path. Node-local configuration — including flags, environment variables, startup configuration, and version-dependent defaults — may gate admission, but must never affect how a committed entry is applied. See [the configuration boundary](docs/technical/architecture/subsystems/fsm/deterministic-fsm.md#34-node-local-configuration-and-rolling-upgrades).
3. **No Pebble reads in the FSM hot path:** apply reads through the cache/command contract; do not introduce storage-read capabilities into the hot path.
3. **No main-store reads in the FSM hot path:** apply reads through the cache/command contract; do not introduce storage-read capabilities into the hot path.
4. **Main-store writes are capability-restricted:** new `OpenWriteSession` call sites require an explicitly justified lifecycle path and must satisfy `.golangci.yaml` enforcement.
5. **Cache entries are not individually evicted:** eviction happens through generation rotation; do not delete individual cache entries outside that mechanism.
6. **FSM reads require declared coverage/preload:** every cache-keyed FSM read must be authorized by the proposal's declared `plan.Coverage`; never widen the read horizon inside apply.
Expand Down
Loading
Loading