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