Files
parrhesia/scripts/run_node_sync_three_node_gap_docker_e2e.sh
self f8cdeb388a
CI / Test (OTP 27.2 / Elixir 1.18.2) (push) Failing after 1m51s
CI / Test (OTP 28.4 / Elixir 1.19.4 + E2E) (push) Failing after 1m36s
feat: page historical sync backfill
Add an internal SYNC-PAGE websocket frame and use it from req_stream sync workers to backfill protected event history deterministically by (created_at, id) before starting the live tail.

The worker discovers a remote cutoff, ingests bounded ascending pages up to that cutoff without advancing the live cursor early, and only advances the cursor when the historical window is complete. The three-node Docker gap scenario now runs req_stream with a small backfill page size to exercise multi-page catch-up.
2026-05-21 13:21:23 +02:00

121 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"
RUN_ID="${PARRHESIA_THREE_NODE_RUN_ID:-three-node-gap-$(date +%s)}"
RUNNER_MIX_ENV="${PARRHESIA_NODE_SYNC_E2E_RUNNER_MIX_ENV:-test}"
IDENTITY_AUTO_GENERATE="${PARRHESIA_NODE_SYNC_E2E_IDENTITY_AUTO_GENERATE:-true}"
if [[ -n "${PARRHESIA_THREE_NODE_TMP_DIR:-}" ]]; then
TMP_DIR="$PARRHESIA_THREE_NODE_TMP_DIR"
else
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/parrhesia-three-node-gap.XXXXXX")"
fi
COMPOSE_FILE="$ROOT_DIR/compose.node-sync-three-node-e2e.yaml"
COMPOSE_PROJECT_SUFFIX="$(basename "$TMP_DIR" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '_')"
COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-parrhesia-three-node-gap-${COMPOSE_PROJECT_SUFFIX}}"
export COMPOSE_PROJECT_NAME
NODE_A_HOST_PORT="${PARRHESIA_NODE_A_HOST_PORT:-45231}"
NODE_B_HOST_PORT="${PARRHESIA_NODE_B_HOST_PORT:-45232}"
NODE_C_HOST_PORT="${PARRHESIA_NODE_C_HOST_PORT:-45233}"
NODE_A_HTTP_URL="http://127.0.0.1:${NODE_A_HOST_PORT}"
NODE_B_HTTP_URL="http://127.0.0.1:${NODE_B_HOST_PORT}"
NODE_C_HTTP_URL="http://127.0.0.1:${NODE_C_HOST_PORT}"
NODE_A_WS_URL="ws://127.0.0.1:${NODE_A_HOST_PORT}/relay"
NODE_B_WS_URL="ws://127.0.0.1:${NODE_B_HOST_PORT}/relay"
NODE_C_WS_URL="ws://127.0.0.1:${NODE_C_HOST_PORT}/relay"
NODE_A_INTERNAL_RELAY_URL="${PARRHESIA_NODE_A_INTERNAL_RELAY_URL:-ws://parrhesia-a:4413/relay}"
NODE_B_INTERNAL_RELAY_URL="${PARRHESIA_NODE_B_INTERNAL_RELAY_URL:-ws://parrhesia-b:4413/relay}"
NODE_C_INTERNAL_RELAY_URL="${PARRHESIA_NODE_C_INTERNAL_RELAY_URL:-ws://parrhesia-c:4413/relay}"
PROTECTED_FILTERS_JSON='[{"kinds":[5000],"#r":["tribes.logs.entry"]}]'
cleanup() {
if [[ "${PARRHESIA_THREE_NODE_KEEP_CONTAINERS:-0}" != "1" ]]; then
docker compose -f "$COMPOSE_FILE" down -v >/dev/null 2>&1 || true
fi
if [[ "${PARRHESIA_THREE_NODE_KEEP_TMP:-0}" != "1" ]]; then
rm -rf "$TMP_DIR"
fi
}
trap cleanup EXIT INT TERM
load_docker_image() {
if [[ -n "${PARRHESIA_IMAGE:-}" ]]; then
return
fi
if [[ "$(uname -s)" != "Linux" ]]; then
echo "PARRHESIA_IMAGE must be set on non-Linux hosts; .#dockerImage is Linux-only." >&2
exit 1
fi
local image_path
image_path="$(nix build .#dockerImage --print-out-paths --no-link)"
docker load <"$image_path" >/dev/null
export PARRHESIA_IMAGE="parrhesia:latest"
}
wait_for_health() {
local url="$1"
local label="$2"
for _ in {1..150}; do
if curl -fsS "$url/health" >/dev/null 2>&1; then
return
fi
sleep 0.2
done
echo "${label} did not become healthy at ${url}" >&2
exit 1
}
load_docker_image
MIX_ENV="$RUNNER_MIX_ENV" mix compile >/dev/null
export PARRHESIA_NODE_A_HOST_PORT="$NODE_A_HOST_PORT"
export PARRHESIA_NODE_B_HOST_PORT="$NODE_B_HOST_PORT"
export PARRHESIA_NODE_C_HOST_PORT="$NODE_C_HOST_PORT"
export PARRHESIA_NODE_A_RELAY_URL="$NODE_A_INTERNAL_RELAY_URL"
export PARRHESIA_NODE_B_RELAY_URL="$NODE_B_INTERNAL_RELAY_URL"
export PARRHESIA_NODE_C_RELAY_URL="$NODE_C_INTERNAL_RELAY_URL"
export PARRHESIA_ACL_PROTECTED_FILTERS="$PROTECTED_FILTERS_JSON"
export PARRHESIA_RELAY_IDENTITY_AUTO_GENERATE="$IDENTITY_AUTO_GENERATE"
export PARRHESIA_NODE_IDENTITY_AUTO_GENERATE="$IDENTITY_AUTO_GENERATE"
mkdir -p "$TMP_DIR"
docker compose -f "$COMPOSE_FILE" up -d db-a db-b db-c
docker compose -f "$COMPOSE_FILE" run -T --rm migrate-a
docker compose -f "$COMPOSE_FILE" run -T --rm migrate-b
docker compose -f "$COMPOSE_FILE" run -T --rm migrate-c
# Recreate the live rollout order: nodes generate local history while isolated,
# then B joins A, B is made unavailable, and C joins through A only.
docker compose -f "$COMPOSE_FILE" up -d parrhesia-a
wait_for_health "$NODE_A_HTTP_URL" "Node A"
export PARRHESIA_THREE_NODE_RUN_ID="$RUN_ID"
export PARRHESIA_NODE_A_HTTP_URL="$NODE_A_HTTP_URL"
export PARRHESIA_NODE_B_HTTP_URL="$NODE_B_HTTP_URL"
export PARRHESIA_NODE_C_HTTP_URL="$NODE_C_HTTP_URL"
export PARRHESIA_NODE_A_WS_URL="$NODE_A_WS_URL"
export PARRHESIA_NODE_B_WS_URL="$NODE_B_WS_URL"
export PARRHESIA_NODE_C_WS_URL="$NODE_C_WS_URL"
export PARRHESIA_NODE_A_RELAY_AUTH_URL="$NODE_A_WS_URL"
export PARRHESIA_NODE_B_RELAY_AUTH_URL="$NODE_B_WS_URL"
export PARRHESIA_NODE_C_RELAY_AUTH_URL="$NODE_C_WS_URL"
export PARRHESIA_NODE_A_SYNC_URL="$NODE_A_INTERNAL_RELAY_URL"
export PARRHESIA_NODE_B_SYNC_URL="$NODE_B_INTERNAL_RELAY_URL"
export PARRHESIA_NODE_C_SYNC_URL="$NODE_C_INTERNAL_RELAY_URL"
export PARRHESIA_THREE_NODE_START_B_CMD="docker compose -f '$COMPOSE_FILE' up -d parrhesia-b"
export PARRHESIA_THREE_NODE_STOP_B_CMD="docker compose -f '$COMPOSE_FILE' stop parrhesia-b"
export PARRHESIA_THREE_NODE_START_C_CMD="docker compose -f '$COMPOSE_FILE' up -d parrhesia-c"
ERL_LIBS="_build/${RUNNER_MIX_ENV}/lib" elixir scripts/node_sync_three_node_gap_e2e.exs
printf 'three-node gap docker run completed\ntmp: %s\n' "$TMP_DIR"