Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
5 changes: 2 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,8 @@
pup
process-compose
lazydocker # a docker compose TUI
# `postgresql` defaults to an older version (15), so we select the latest version (16)
# explicitly.
postgresql_16
redis # keydb
postgresql_18

# Figures
graphviz
Expand Down
78 changes: 22 additions & 56 deletions process-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "3"

# When adding/removing native processes (non-docker commands), update the NATIVE_PROCESSES
# array in scripts/cleanup-process-compose to ensure proper cleanup on test failures.
# When adding/removing processes, update the NATIVE_PROCESSES array in
# scripts/cleanup-process-compose to ensure proper cleanup on test failures.

x-availability:
# terminate process compose if process exits
Expand All @@ -21,13 +21,12 @@ environment:
- ESPRESSO_L1_PROVIDER=http://localhost:${ESPRESSO_L1_PORT}
- ESPRESSO_NODE_GENESIS_FILE=${ESPRESSO_NODE_GENESIS_FILE:-data/genesis/demo.toml}
- ESPRESSO_STATE_RELAY_SERVER_URL=http://localhost:${ESPRESSO_STATE_RELAY_SERVER_PORT}
- QUERY_SERVICE_URI=http://localhost:${ESPRESSO_NODE_1_API_PORT}/v0/
- NODE_VALIDATOR_URI=ws://localhost:${ESPRESSO_NODE_VALIDATOR_PORT}/v0/
processes:
# Cheating a bit here but since we don't usually have to debug go-ethereum
# it's using the docker compose service which is a bit easier.
demo-l1-network:
command: docker compose up demo-l1-network --force-recreate --renew-anon-volumes
command: >-
anvil --host 0.0.0.0 --port ${ESPRESSO_L1_PORT} --chain-id 31337
--accounts 20 --balance 1000000000 --block-time 1
--slots-in-an-epoch 32
Comment thread
sveitser marked this conversation as resolved.
readiness_probe:
exec:
command: >-
Expand Down Expand Up @@ -203,16 +202,6 @@ processes:
path: /healthcheck
availability: *run-forever

telemetry-collector:
command: docker compose up vector --force-recreate --renew-anon-volumes
log_location: logs/vector.log
# Telemetry is not load-bearing: a failure (e.g. Docker Hub pull timeout in CI)
# must not tear down the whole demo like *run-forever would.
availability:
restart: on_failure
backoff_seconds: 5
max_restarts: 5

prover-one-shot:
# "nice" this compute heavy process to reduce load
command: nice state-prover
Expand Down Expand Up @@ -304,7 +293,7 @@ processes:
command: espresso-node -- storage-sql -- http -- config -- query -- submit -- explorer -- catchup
environment:
- ESPRESSO_NODE_TELEMETRY_METRICS_ENABLE=true
- ESPRESSO_NODE_TELEMETRY_ENDPOINT=http://localhost:${ESPRESSO_DEMO_TELEMETRY_PRW_PORT}
- ESPRESSO_NODE_TELEMETRY_ENDPOINT=http://intentionally-unreachable-telemetry-endpoint
- ESPRESSO_NODE_API_PORT=${ESPRESSO_NODE_1_API_PORT}
- ESPRESSO_NODE_AXUM_PORT=${ESPRESSO_NODE_1_AXUM_PORT}
- ESPRESSO_NODE_TONIC_PORT=${ESPRESSO_NODE_1_TONIC_PORT}
Expand Down Expand Up @@ -334,7 +323,8 @@ processes:
- ESPRESSO_NODE_IDENTITY_LATITUDE=39.0742
- ESPRESSO_NODE_IDENTITY_LONGITUDE=21.8243
- ESPRESSO_NODE_PUBLIC_API_URL=http://localhost:${ESPRESSO_NODE_1_API_PORT}/
- ESPRESSO_L1_WS_PROVIDER=ws://localhost:${ESPRESSO_L1_WS_PORT}
# Anvil serves WS on the same port as HTTP (no separate ESPRESSO_L1_WS_PORT).
- ESPRESSO_L1_WS_PROVIDER=ws://localhost:${ESPRESSO_L1_PORT}
depends_on:
orchestrator:
condition: process_healthy
Expand Down Expand Up @@ -423,7 +413,7 @@ processes:
command: espresso-node -- http -- config -- query -- storage-fs
environment:
- ESPRESSO_NODE_TELEMETRY_LOGS_ENABLE=true
- ESPRESSO_NODE_TELEMETRY_ENDPOINT=http://localhost:${ESPRESSO_DEMO_TELEMETRY_OTLP_PORT}
- ESPRESSO_NODE_TELEMETRY_ENDPOINT=http://intentionally-unreachable-telemetry-endpoint
- ESPRESSO_NODE_TELEMETRY_LOG=info
- ESPRESSO_NODE_API_PORT=${ESPRESSO_NODE_3_API_PORT}
- ESPRESSO_NODE_AXUM_PORT=${ESPRESSO_NODE_3_AXUM_PORT}
Expand All @@ -449,7 +439,8 @@ processes:
- ESPRESSO_NODE_IDENTITY_LATITUDE=35.8617
- ESPRESSO_NODE_IDENTITY_LONGITUDE=104.1954
- ESPRESSO_NODE_PUBLIC_API_URL=http://localhost:${ESPRESSO_NODE_3_API_PORT}/
- ESPRESSO_L1_WS_PROVIDER=ws://localhost:${ESPRESSO_L1_WS_PORT}
# Anvil serves WS on the same port as HTTP (no separate ESPRESSO_L1_WS_PORT).
- ESPRESSO_L1_WS_PROVIDER=ws://localhost:${ESPRESSO_L1_PORT}
depends_on:
orchestrator:
condition: process_healthy
Expand Down Expand Up @@ -564,13 +555,9 @@ processes:
failure_threshold: 100
availability: *run-forever

# We use KeyDB (a Redis variant) to maintain consistency between
# different parts of the CDN
# Cheating a bit here too, but KeyDB is not available as a Nix package.
# Could do local (SQLite) discovery, but removes some of the spirit
# from the local demo.
# Redis backs CDN discovery (shared state between marshal and brokers).
keydb:
command: docker run --rm -p 0.0.0.0:6379:6379 eqalpha/keydb --requirepass changeme!
command: redis-server --port 6379 --requirepass 'changeme!'
readiness_probe:
exec:
command: nc -zv localhost 6379
Expand Down Expand Up @@ -719,48 +706,27 @@ processes:
availability: *run-forever

espresso-node-db-0:
command:
docker run -e POSTGRES_PASSWORD -e POSTGRES_USER -e POSTGRES_DB -p ${ESPRESSO_NODE_0_DB_PORT}:5432 postgres
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_USER=root
- POSTGRES_DB=espresso
command: scripts/run-postgres ${ESPRESSO_NODE_0_DB_PORT} ${ESPRESSO_BASE_STORAGE_PATH}/pg0
readiness_probe:
exec:
command: pg_isready -h localhost -p ${ESPRESSO_NODE_0_DB_PORT}
# Gate on the espresso DB: the server accepts connections before run-postgres runs
# createdb, so a plain pg_isready would report ready too early.
command: psql -h localhost -p ${ESPRESSO_NODE_0_DB_PORT} -U root -d espresso -c 'SELECT 1'
initial_delay_seconds: 5
period_seconds: 5
timeout_seconds: 4
# Postgres can be falsely "ready" once before running init scripts.
# See https://github.com/docker-library/postgres/issues/146 for discussion.
success_threshold: 2
failure_threshold: 20
availability: *run-forever

espresso-node-db-1:
command:
docker run -e POSTGRES_PASSWORD -e POSTGRES_USER -e POSTGRES_DB -p ${ESPRESSO_NODE_1_DB_PORT}:5432 postgres
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_USER=root
- POSTGRES_DB=espresso
command: scripts/run-postgres ${ESPRESSO_NODE_1_DB_PORT} ${ESPRESSO_BASE_STORAGE_PATH}/pg1
readiness_probe:
exec:
command: pg_isready -h localhost -p ${ESPRESSO_NODE_1_DB_PORT}
# Gate on the espresso DB: the server accepts connections before run-postgres runs
# createdb, so a plain pg_isready would report ready too early.
command: psql -h localhost -p ${ESPRESSO_NODE_1_DB_PORT} -U root -d espresso -c 'SELECT 1'
initial_delay_seconds: 5
period_seconds: 5
timeout_seconds: 4
# Postgres can be falsely "ready" once before running init scripts.
# See https://github.com/docker-library/postgres/issues/146 for discussion.
success_threshold: 2
failure_threshold: 20
availability: *run-forever

block-explorer:
command:
docker run --rm -p ${ESPRESSO_BLOCK_EXPLORER_PORT}:3000 -e QUERY_SERVICE_URI -e NODE_VALIDATOR_URI
ghcr.io/espressosystems/espresso-block-explorer:main
depends_on:
espresso-node-1:
condition: process_healthy
availability: *run-forever
12 changes: 5 additions & 7 deletions scripts/cleanup-process-compose
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
#
# Cleanup script for espresso-network demo
#
# This script safely terminates all processes and containers started by the native demo.
# This script safely terminates all processes started by the native demo.
# It's called automatically by scripts/demo-native when the demo exits (via trap).
#
# Key features:
# - Only kills processes running from this project directory (checks /proc/$pid/cwd)
# - Cleans up Docker containers and networks
# - Safe to run multiple times
#
set -e
Expand Down Expand Up @@ -56,6 +55,9 @@ NATIVE_PROCESSES=(
state-prover
espresso-bridge
staking-cli
anvil
postgres
redis-server
)

# Helper function to safely kill processes only if they're running from this project
Expand Down Expand Up @@ -86,11 +88,7 @@ kill_project_processes "process-compose" TERM
sleep 2
kill_project_processes "process-compose" KILL

# 2. Stop all docker containers via docker compose
echo "Stopping docker services..."
docker compose down --remove-orphans --volumes 2>/dev/null || echo "docker-compose not running or already stopped"

# 3. Kill lingering native processes (only if running from this project directory)
# 2. Kill lingering native processes (only if running from this project directory)
echo "Killing native processes from $PROJECT_DIR..."
for proc in "${NATIVE_PROCESSES[@]}"; do
kill_project_processes "$proc" TERM
Expand Down
75 changes: 75 additions & 0 deletions scripts/run-postgres
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
Comment thread
imabdulbasit marked this conversation as resolved.
#
# Start a single native PostgreSQL instance for the process-compose demo.
#
# The caller provides a port and a data directory; this script initializes
# the cluster, starts postgres bound to 127.0.0.1, creates the "espresso"
# database, and then blocks in the foreground so process-compose can manage
# it as one long-lived service. SIGTERM/SIGINT are forwarded to postgres so
# process-compose can stop it cleanly. The data directory is expected to
# live under a caller-managed temp path; it is not created or removed here.
#
set -euo pipefail

readonly SCRIPT_NAME="${0##*/}"

usage() {
cat <<EOF
Usage: ${SCRIPT_NAME} PORT DATADIR

Start a native postgres instance bound to 127.0.0.1:PORT, storing data
and the unix socket under DATADIR, with a "root" superuser (trust auth)
and an "espresso" database.

ARGUMENTS:
PORT TCP port for postgres to listen on
DATADIR Existing empty data directory to initialize and use

OPTIONS:
-h, --help Show this help
Comment on lines +19 to +20

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor doc nit: the usage says DATADIR Existing empty data directory to initialize and use, but the script actually relies on initdb creating the directory (the caller passes ${ESPRESSO_BASE_STORAGE_PATH}/pg0, which doesn't exist yet). Only the parent needs to exist. Consider rewording to "Data directory to initialize (created if missing; must be empty if it exists)" so it's clear the dir need not pre-exist — and that re-running against a populated dir would fail initdb.

EOF
}

err() {
echo "[${SCRIPT_NAME}] $*" >&2
}

main() {
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi

if [[ $# -ne 2 ]]; then
err "Expected 2 arguments, got $#"
usage >&2
exit 1
fi

local port="$1"
local datadir="$2"
local pg_pid

initdb -D "${datadir}" -U root --auth=trust >/dev/null

postgres -D "${datadir}" -p "${port}" -k "${datadir}" -h 127.0.0.1 &
pg_pid=$!

# SIGINT triggers postgres "fast shutdown" (rolls back and disconnects clients
# immediately). SIGTERM would be a "smart shutdown" that blocks until every client
# disconnects, hanging until process-compose force-kills on teardown.
trap 'kill -INT "${pg_pid}" 2>/dev/null' TERM INT

until pg_isready -h 127.0.0.1 -p "${port}" >/dev/null 2>&1; do
sleep 0.5
done

createdb -h 127.0.0.1 -p "${port}" -U root espresso

# Double wait: the first returns as soon as the trap fires; the second blocks until

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Startup-time shutdown can hang this script. The trap is installed before this readiness loop, but if SIGTERM/SIGINT arrives while we're still in the until pg_isready loop, the trap fires kill -INT on postgres and postgres starts shutting down — after which pg_isready will never succeed, so this loop spins forever and the wait lines below are never reached. process-compose then has to SIGKILL on teardown.

This is a narrow window (only during startup, before the DB is ready), so it's minor, but you can make shutdown deterministic by breaking out when a shutdown was requested, e.g. set a flag in the trap and check it:

shutting_down=0
trap 'shutting_down=1; kill -INT "${pg_pid}" 2>/dev/null' TERM INT

until pg_isready -h 127.0.0.1 -p "${port}" >/dev/null 2>&1; do
  [[ "${shutting_down}" -eq 1 ]] && break
  sleep 0.5
done

# postgres has actually exited so it is never left orphaned (see scripts/demo-native).
wait "${pg_pid}" || true
wait "${pg_pid}" 2>/dev/null || true
}

main "$@"
Loading