run-relay-pprof.sh raw

   1  #!/usr/bin/env bash
   2  set -euo pipefail
   3  
   4  # Run the relay with CPU profiling enabled, wait 60s, then open the
   5  # generated profile using `go tool pprof` web UI.
   6  #
   7  # Notes:
   8  # - Builds a temporary relay binary in /tmp and deletes it on exit.
   9  # - Uses the exact env requested, plus ORLY_PPROF=cpu and a deterministic
  10  #   ORLY_PPROF_PATH inside a temp dir.
  11  # - Profiles for DURATION seconds (default 60).
  12  # - Launches pprof web UI at http://localhost:8000 and attempts to open browser.
  13  
  14  DURATION="${DURATION:-60}"
  15  HEALTH_PORT="${HEALTH_PORT:-18081}"
  16  ROOT_DIR="/home/mleku/src/next.orly.dev"
  17  LISTEN_HOST="${LISTEN_HOST:-10.0.0.2}"
  18  
  19  cd "$ROOT_DIR"
  20  
  21  # Refresh embedded web assets
  22  reset || true
  23  ./scripts/update-embedded-web.sh || true
  24  
  25  TMP_DIR="$(mktemp -d -t orly-pprof-XXXXXX)"
  26  BIN_PATH="$TMP_DIR/next.orly.dev"
  27  LOG_FILE="$TMP_DIR/relay.log"
  28  PPROF_FILE=""
  29  RELAY_PID=""
  30  PPROF_DIR="$TMP_DIR/profiles"
  31  mkdir -p "$PPROF_DIR"
  32  
  33  cleanup() {
  34    # Try to stop relay if still running
  35    if [[ -n "${RELAY_PID}" ]] && kill -0 "${RELAY_PID}" 2>/dev/null; then
  36      kill "${RELAY_PID}" || true
  37      wait "${RELAY_PID}" || true
  38    fi
  39    rm -f "$BIN_PATH" 2>/dev/null || true
  40    rm -rf "$TMP_DIR" 2>/dev/null || true
  41  }
  42  trap cleanup EXIT
  43  
  44  echo "[run-relay-pprof] Building relay binary ..."
  45  GOFLAGS="${GOFLAGS:-}" go build -o "$BIN_PATH" .
  46  
  47  echo "[run-relay-pprof] Starting relay with CPU profiling ..."
  48  (
  49    ORLY_LOG_LEVEL=debug \
  50    ORLY_LISTEN="$LISTEN_HOST" \
  51    ORLY_PORT=3334 \
  52    ORLY_ADMINS=npub1fjqqy4a93z5zsjwsfxqhc2764kvykfdyttvldkkkdera8dr78vhsmmleku \
  53    ORLY_ACL_MODE=follows \
  54    ORLY_RELAY_ADDRESSES=test.orly.dev \
  55    ORLY_IP_BLACKLIST=192.71.213.188 \
  56    ORLY_HEALTH_PORT="$HEALTH_PORT" \
  57    ORLY_ENABLE_SHUTDOWN=true \
  58    ORLY_PPROF_HTTP=true \
  59    ORLY_OPEN_PPROF_WEB=true \
  60    "$BIN_PATH"
  61  ) >"$LOG_FILE" 2>&1 &
  62  RELAY_PID=$!
  63  
  64  # Wait for pprof HTTP server readiness
  65  PPROF_BASE="http://${LISTEN_HOST}:6060"
  66  echo "[run-relay-pprof] Waiting for pprof at ${PPROF_BASE} ..."
  67  for i in {1..100}; do
  68    if curl -fsS "${PPROF_BASE}/debug/pprof/" -o /dev/null 2>/dev/null; then
  69      READY=1
  70      break
  71    fi
  72    sleep 0.2
  73  done
  74  if [[ -z "${READY:-}" ]]; then
  75    echo "[run-relay-pprof] ERROR: pprof HTTP server not reachable at ${PPROF_BASE}." >&2
  76    echo "[run-relay-pprof]       Check that ${LISTEN_HOST} is a local bindable address." >&2
  77    # Attempt to dump recent logs for context
  78    tail -n 100 "$LOG_FILE" || true
  79    # Try INT to clean up
  80    killall -INT next.orly.dev 2>/dev/null || true
  81    exit 1
  82  fi
  83  
  84  # Open the HTTP pprof UI
  85  ( xdg-open "${PPROF_BASE}/debug/pprof/" >/dev/null 2>&1 || true ) &
  86  
  87  echo "[run-relay-pprof] Collecting CPU profile via HTTP for ${DURATION}s ..."
  88  # The HTTP /debug/pprof/profile endpoint records CPU for the provided seconds
  89  # and returns a pprof file without needing to stop the process.
  90  curl -fsS --max-time $((DURATION+10)) \
  91    "${PPROF_BASE}/debug/pprof/profile?seconds=${DURATION}" \
  92    -o "$PPROF_DIR/cpu.pprof" || true
  93  
  94  echo "[run-relay-pprof] Sending SIGINT (Ctrl+C) for graceful shutdown ..."
  95  killall -INT next.orly.dev 2>/dev/null || true
  96  
  97  # Wait up to ~60s for graceful shutdown so defers (pprof Stop) can run
  98  for i in {1..300}; do
  99    if ! pgrep -x next.orly.dev >/dev/null 2>&1; then
 100      break
 101    fi
 102    sleep 0.2
 103  done
 104  
 105  # Try HTTP shutdown if still running (ensures defer paths can run)
 106  if pgrep -x next.orly.dev >/dev/null 2>&1; then
 107    echo "[run-relay-pprof] Still running, requesting /shutdown ..."
 108    curl -fsS --max-time 2 "http://10.0.0.2:${HEALTH_PORT}/shutdown" >/dev/null 2>&1 || true
 109    for i in {1..150}; do
 110      if ! pgrep -x next.orly.dev >/dev/null 2>&1; then
 111        break
 112      fi
 113      sleep 0.2
 114    done
 115  fi
 116  if pgrep -x next.orly.dev >/dev/null 2>&1; then
 117    echo "[run-relay-pprof] Escalating: sending SIGTERM ..."
 118    killall -TERM next.orly.dev 2>/dev/null || true
 119    for i in {1..150}; do
 120      if ! pgrep -x next.orly.dev >/dev/null 2>&1; then
 121        break
 122      fi
 123      sleep 0.2
 124    done
 125  fi
 126  if pgrep -x next.orly.dev >/dev/null 2>&1; then
 127    echo "[run-relay-pprof] Force kill: sending SIGKILL ..."
 128    killall -KILL next.orly.dev 2>/dev/null || true
 129  fi
 130  
 131  PPROF_FILE="$PPROF_DIR/cpu.pprof"
 132  if [[ ! -s "$PPROF_FILE" ]]; then
 133    echo "[run-relay-pprof] ERROR: HTTP CPU profile not captured (file empty)." >&2
 134    echo "[run-relay-pprof] Hint: Ensure ORLY_PPROF_HTTP=true and port 6060 is reachable." >&2
 135    exit 1
 136  fi
 137  
 138  echo "[run-relay-pprof] Detected profile file: $PPROF_FILE"
 139  echo "[run-relay-pprof] Launching 'go tool pprof' web UI on :8000 ..."
 140  
 141  # Try to open a browser automatically, ignore failures
 142  ( sleep 0.6; xdg-open "http://localhost:8000" >/dev/null 2>&1 || true ) &
 143  
 144  exec go tool pprof -http=:8000 "$BIN_PATH" "$PPROF_FILE"
 145  
 146  
 147