fix: resolve AsyncLocalStorage + dir issues in custom server, update benchmark script

Co-authored-by: ajnart <49837342+ajnart@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]andajnart committed 2026-02-28 09:13:41 +00:00
1 parent c8ae7f283a
commit 8994bdedc3
3 files changed
+120 -80

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"build": "pnpm with-env next build && pnpm build:custom-server",
"build:custom-server": "esbuild server.ts --bundle --platform=node --outfile=customServer.cjs --external:bcrypt --external:@opentelemetry/api --external:deasync --external:cpu-features --loader:.html=text --loader:.scss=text --loader:.node=text",
"build:custom-server": "esbuild server.ts --bundle --platform=node --outfile=customServer.cjs --external:next --external:bcrypt --external:@opentelemetry/api --external:deasync --external:cpu-features --loader:.html=text --loader:.scss=text --loader:.node=text",
"clean": "git clean -xdf .next .turbo node_modules",
"dev": "pnpm with-env tsx ./server.ts",
"format": "prettier --check . --ignore-path ../../.gitignore",
+7 -1
View File
@@ -1,4 +1,8 @@
// Must be imported first to set up globalThis.AsyncLocalStorage for Next.js
import "next/dist/server/node-environment-baseline";
import { createServer } from "http";
import path from "path";
import next from "next";
import { parse } from "url";
import { applyWSSHandler } from "@trpc/server/adapters/ws";
@@ -15,8 +19,10 @@ const logger = createLogger({ module: "customServer" });
const dev = process.env.NODE_ENV !== "production";
const hostname = process.env.HOSTNAME || "localhost";
const port = parseInt(process.env.PORT || "3000", 10);
// Resolve the directory of this file so Next.js can find the app/pages dirs
const dir = path.resolve(__dirname);
const app = next({ dev, hostname, port });
const app = next({ dev, hostname, port, dir });
const handle = app.getRequestHandler();
app.prepare().then(() => {
+112 -78
View File
@@ -1,114 +1,148 @@
#!/usr/bin/env bash
# Benchmark memory usage of the WebSocket server approach
# Usage: ./scripts/benchmark-memory.sh [approach]
# approach: "separate" (current: separate ws server) or "integrated" (custom Next.js server)
# Benchmark memory usage: separate ws server vs integrated custom Next.js server
# Usage: ./scripts/benchmark-memory.sh [approach] [wait_seconds]
# approach: "separate" | "integrated" | "both" (default: both)
set -e
APPROACH="${1:-separate}"
WAIT_SECONDS="${2:-10}"
APPROACH="${1:-both}"
WAIT_SECONDS="${2:-15}"
echo "============================================"
echo "Memory Benchmark: $APPROACH approach"
echo "============================================"
echo ""
measure_memory() {
local pid=$1
measure_process_tree_memory() {
local root_pid=$1
local label=$2
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
local rss
rss=$(ps -o rss= -p "$pid" 2>/dev/null | tr -d ' ')
if [ -n "$rss" ]; then
local rss_mb
rss_mb=$(echo "scale=2; $rss / 1024" | bc)
echo "$label (PID $pid): ${rss_mb} MB (RSS: ${rss} KB)"
echo "$rss"
else
echo "$label (PID $pid): unable to read memory"
echo "0"
fi
else
local total_rss=0
if [ -z "$root_pid" ] || ! kill -0 "$root_pid" 2>/dev/null; then
echo "$label: not running"
echo "0"
return
fi
# Measure root process
local root_rss
root_rss=$(grep VmRSS /proc/$root_pid/status 2>/dev/null | awk '{print $2}')
root_rss=${root_rss:-0}
total_rss=$root_rss
local root_mb
root_mb=$(echo "scale=2; $root_rss / 1024" | bc)
echo " $label (PID $root_pid): ${root_mb} MB"
# Measure child processes recursively
for cpid in $(pgrep -P "$root_pid" 2>/dev/null); do
if kill -0 "$cpid" 2>/dev/null; then
local child_rss
child_rss=$(grep VmRSS /proc/$cpid/status 2>/dev/null | awk '{print $2}')
child_rss=${child_rss:-0}
total_rss=$((total_rss + child_rss))
local child_mb
child_mb=$(echo "scale=2; $child_rss / 1024" | bc)
local child_cmd
child_cmd=$(cat /proc/$cpid/cmdline 2>/dev/null | tr '\0' ' ' | head -c 60)
echo " Child (PID $cpid): ${child_mb} MB - $child_cmd"
# Grandchildren
for gpid in $(pgrep -P "$cpid" 2>/dev/null); do
if kill -0 "$gpid" 2>/dev/null; then
local gchild_rss
gchild_rss=$(grep VmRSS /proc/$gpid/status 2>/dev/null | awk '{print $2}')
gchild_rss=${gchild_rss:-0}
total_rss=$((total_rss + gchild_rss))
local gchild_mb
gchild_mb=$(echo "scale=2; $gchild_rss / 1024" | bc)
local gchild_cmd
gchild_cmd=$(cat /proc/$gpid/cmdline 2>/dev/null | tr '\0' ' ' | head -c 60)
echo " Grandchild (PID $gpid): ${gchild_mb} MB - $gchild_cmd"
fi
done
fi
done
local total_mb
total_mb=$(echo "scale=2; $total_rss / 1024" | bc)
echo " Subtotal: ${total_mb} MB"
# Export for caller
eval "${label}_TOTAL_RSS=$total_rss"
}
cleanup() {
echo ""
echo "Cleaning up processes..."
for pid in "${PIDS[@]}"; do
cleanup_pids() {
for pid in "$@"; do
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null || true
fi
done
wait 2>/dev/null || true
echo "Done."
}
trap cleanup EXIT
run_separate() {
echo "============================================"
echo "SEPARATE APPROACH (Current: WS + Next.js)"
echo "============================================"
PIDS=()
TOTAL_RSS=0
if [ "$APPROACH" = "separate" ]; then
echo "Starting separate WebSocket server (current approach)..."
echo "---"
# Start the WebSocket server
# Start WS server
node apps/websocket/wssServer.cjs &
WSS_PID=$!
PIDS+=("$WSS_PID")
WS_PID=$!
sleep 5
# Start the Next.js server
node apps/nextjs/server.js &
NEXTJS_PID=$!
PIDS+=("$NEXTJS_PID")
WS_LISTEN_PID=$(lsof -i :3001 -t 2>/dev/null | head -1)
echo "WS server listening PID: $WS_LISTEN_PID"
echo "Waiting ${WAIT_SECONDS}s for processes to stabilize..."
sleep "$WAIT_SECONDS"
WS_RSS=$(grep VmRSS /proc/${WS_LISTEN_PID}/status 2>/dev/null | awk '{print $2}')
WS_RSS=${WS_RSS:-0}
WS_MB=$(echo "scale=2; $WS_RSS / 1024" | bc)
echo " WebSocket Server: ${WS_MB} MB"
echo ""
echo "--- Memory Usage (Separate Approach) ---"
wss_mem=$(measure_memory "$WSS_PID" "WebSocket Server" | tail -1)
nextjs_mem=$(measure_memory "$NEXTJS_PID" "Next.js Server" | tail -1)
echo "TOTAL (Separate WS only): ${WS_MB} MB"
echo "Note: Next.js dev server not included (would add ~1.5-2.5 GB)"
echo "============================================"
TOTAL_RSS=$((wss_mem + nextjs_mem))
TOTAL_MB=$(echo "scale=2; $TOTAL_RSS / 1024" | bc)
cleanup_pids "$WS_PID"
SEPARATE_RSS=$WS_RSS
}
echo ""
echo "TOTAL: ${TOTAL_MB} MB (RSS: ${TOTAL_RSS} KB)"
run_integrated() {
echo "============================================"
echo "INTEGRATED APPROACH (Custom Next.js Server)"
echo "============================================"
elif [ "$APPROACH" = "integrated" ]; then
echo "Starting integrated custom Next.js server (new approach)..."
echo "---"
# Start the custom server (Next.js + WebSocket integrated)
node apps/nextjs/customServer.cjs &
cd apps/nextjs
node customServer.cjs &
CUSTOM_PID=$!
PIDS+=("$CUSTOM_PID")
cd ../..
echo "Waiting ${WAIT_SECONDS}s for process to stabilize..."
echo "Waiting ${WAIT_SECONDS}s for server to stabilize..."
sleep "$WAIT_SECONDS"
measure_process_tree_memory "$CUSTOM_PID" "CustomServer"
echo ""
echo "--- Memory Usage (Integrated Approach) ---"
custom_mem=$(measure_memory "$CUSTOM_PID" "Custom Server (Next.js + WS)" | tail -1)
INTEGRATED_RSS=${CustomServer_TOTAL_RSS:-0}
INTEGRATED_MB=$(echo "scale=2; $INTEGRATED_RSS / 1024" | bc)
echo "TOTAL (Integrated): ${INTEGRATED_MB} MB"
echo "============================================"
TOTAL_RSS=$custom_mem
TOTAL_MB=$(echo "scale=2; $TOTAL_RSS / 1024" | bc)
cleanup_pids "$CUSTOM_PID"
}
if [ "$APPROACH" = "separate" ] || [ "$APPROACH" = "both" ]; then
run_separate
echo ""
echo "TOTAL: ${TOTAL_MB} MB (RSS: ${TOTAL_RSS} KB)"
else
echo "Unknown approach: $APPROACH"
echo "Usage: $0 [separate|integrated] [wait_seconds]"
exit 1
fi
echo ""
echo "============================================"
echo "Benchmark complete for: $APPROACH"
echo "Total memory: ${TOTAL_MB} MB"
echo "============================================"
if [ "$APPROACH" = "integrated" ] || [ "$APPROACH" = "both" ]; then
# Clean up lock from previous run
rm -rf apps/nextjs/.next/dev/lock
run_integrated
echo ""
fi
if [ "$APPROACH" = "both" ]; then
echo "============================================"
echo "COMPARISON"
echo "============================================"
SEP_MB=$(echo "scale=2; ${SEPARATE_RSS:-0} / 1024" | bc)
INT_MB=$(echo "scale=2; ${INTEGRATED_RSS:-0} / 1024" | bc)
echo "Separate WS server only: ${SEP_MB} MB"
echo "Integrated custom server: ${INT_MB} MB"
echo "(The integrated server includes both Next.js AND WebSocket)"
echo "============================================"
fi