Thanks to visit codestin.com
Credit goes to github.com

Skip to content

stateless-me/paxon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Paxon

Single-binary CASPaxos acceptor + CLI (KV + config + dump) with:

  • UDP transport, big-endian wire format
  • HLC (Hybrid Logical Clock) folded into ballots
  • effective_promise = max(promise, accepted.ballot)
  • Atomic RocksDB WriteBatch on ACCEPT
  • Read-only fast path (1‑RTT READ) + fallback Phase‑1
  • Joint reconfiguration (OldOnly → Joint → NewOnly)
  • Seed bootstrap (learn config c on startup if CF_SYS empty)
  • Namespaces: NS_CFG='c' (single config key), NS_DATA='d' (user keys)

paxon.c contains both the acceptor and the UDP‑speaking CLI.


Build

You need RocksDB and its C API (rocksdb/c.h) plus common compression libs.

macOS (Homebrew)

brew install rocksdb snappy lz4 zstd bzip2
make                    # or: make ROCKSDB_PREFIX=/opt/homebrew

Linux (Debian/Ubuntu)

sudo apt-get install -y build-essential librocksdb-dev zlib1g-dev libbz2-dev libsnappy-dev liblz4-dev libzstd-dev
make

If RocksDB isn’t in a default path, point the Makefile at it:

make ROCKSDB_PREFIX=/custom/prefix

Install / clean:

sudo make install
make clean

Usage

Start acceptors

Run 2–3 acceptors (each with its own RocksDB dir):

# Terminal 1
make run-acceptor BIND=127.0.0.1:9000 DB=./db1

# Terminal 2
make run-acceptor BIND=127.0.0.1:9001 DB=./db2 SEEDS="127.0.0.1:9000"

# Terminal 3
make run-acceptor BIND=127.0.0.1:9002 DB=./db3 SEEDS="127.0.0.1:9000 127.0.0.1:9001"

Note: If CF_SYS (the local mirror of the chosen config) is empty, an acceptor will try to bootstrap from --seed peers by reading the config key {'c'}.

Bootstrap the cluster config

Push a human‑readable config (replicated under key {'c'}):

make cfg-set \
  CFG='epoch=1
phase=old
old=127.0.0.1:9000,127.0.0.1:9001,127.0.0.1:9002' \
  SEEDS="127.0.0.1:9000 127.0.0.1:9001 127.0.0.1:9002"

Read it back:

make cfg-get SEEDS="127.0.0.1:9000 127.0.0.1:9001 127.0.0.1:9002"

KV operations

# Set
make kv-set KEY=foo VAL=bar SEEDS="127.0.0.1:9000 127.0.0.1:9001 127.0.0.1:9002"

# Get
make kv-get KEY=foo SEEDS="127.0.0.1:9000 127.0.0.1:9001 127.0.0.1:9002"

The CLI talks UDP directly to the acceptors. GET attempts the read‑only fast path (1‑RTT) and falls back to Phase‑1 if needed.

Reconfiguration (joint consensus)

Example: migrate from {9000,9001} to {9001,9002}.

  1. Enter joint:
make cfg-set \
  CFG='epoch=2
phase=joint
old=127.0.0.1:9000,127.0.0.1:9001
new=127.0.0.1:9001,127.0.0.1:9002' \
  SEEDS="127.0.0.1:9000 127.0.0.1:9001 127.0.0.1:9002"
  1. Finalize to new (after clients have learned the new epoch/phase):
make cfg-set \
  CFG='epoch=2
phase=new
new=127.0.0.1:9001,127.0.0.1:9002' \
  SEEDS="127.0.0.1:9001 127.0.0.1:9002"

On-disk layout (RocksDB)

  • Column Families:

    • CF_STATE — protocol state
      • p:<rawkey> → 24B promise (ballot)
      • a:<rawkey> → 24B ballot + 4B vlen + value
    • CF_SYS — mirror of chosen config under key "cfg" (fast restarts)
  • Namespaces (first byte of the replicated “raw key”):

    • NS_CFG = 'c' — single config key
    • NS_DATA = 'd' — user KV keys ('d' + user_key)

Wire & ballots

  • Big‑endian fields on the wire.
  • header_t carries: msg_type, key_len, value_len, epoch, proposer_id, proposer_age, and HLC (physical ms + logical).
  • Ballot = (epoch, counter, proposer_id) with counter folded from HLC.
    Local effective floor = max(promise, accepted.ballot); proposals below the floor are rejected.

Message types:

  • PREPARE → PROMISE | REJECT
  • ACCEPT → ACCEPTED | REJECT
  • READ → READ_RSP | REJECT (fast path)

Debugging

Dump local RocksDB state:

./paxon dump --db ./db1

You’ll see p: and a: entries plus the mirrored config in CF_SYS.


License (Apache-2.0)

This project is licensed under the Apache License, Version 2.0.