@@ -99,17 +99,37 @@ git -C "$SOAK_PROJECT" -c user.email=test@test -c user.name=test commit -q -m "i
9999
100100# ── Helper: run CLI tool call and record latency ─────────────────
101101
102- cli_call () {
102+ # Query ID counter
103+ QUERY_ID=1
104+
105+ # Send a JSON-RPC tool call to the running server via its stdin pipe.
106+ # Reads response from server stdout. Records latency.
107+ mcp_call () {
103108 local tool=" $1 "
104109 local args=" $2 "
110+ local id=$QUERY_ID
111+ QUERY_ID=$(( QUERY_ID + 1 ))
112+
113+ local req=" {\" jsonrpc\" :\" 2.0\" ,\" id\" :$id ,\" method\" :\" tools/call\" ,\" params\" :{\" name\" :\" $tool \" ,\" arguments\" :$args }}"
105114 local t0
106115 t0=$( python3 -c " import time; print(int(time.time()*1000))" )
107- " $BINARY " cli " $tool " " $args " > /dev/null 2>&1
108- local rc=$?
109- local t1
110- t1=$( python3 -c " import time; print(int(time.time()*1000))" )
111- local dur=$(( t1 - t0 ))
112- echo " $( date +%s) ,$tool ,$dur ,$rc " >> " $LATENCY_CSV "
116+
117+ # Send request to server stdin
118+ echo " $req " >&3
119+
120+ # Read response (wait up to 30s)
121+ local resp=" "
122+ if read -t 30 resp < & 4 2> /dev/null; then
123+ local t1
124+ t1=$( python3 -c " import time; print(int(time.time()*1000))" )
125+ local dur=$(( t1 - t0 ))
126+ echo " $( date +%s) ,$tool ,$dur ,0" >> " $LATENCY_CSV "
127+ else
128+ local t1
129+ t1=$( python3 -c " import time; print(int(time.time()*1000))" )
130+ local dur=$(( t1 - t0 ))
131+ echo " $( date +%s) ,$tool ,$dur ,1" >> " $LATENCY_CSV "
132+ fi
113133}
114134
115135# ── Helper: collect diagnostics snapshot ─────────────────────────
@@ -131,23 +151,35 @@ collect_snapshot() {
131151# ── Phase 1: Start MCP server with diagnostics ──────────────────
132152
133153echo " --- Phase 1: start server ---"
134- # Keep stdin open with a sleeping process — kill it to send EOF
135- sleep 999999 | CBM_DIAGNOSTICS=1 " $BINARY " > /dev/null 2> " $RESULTS_DIR /server-stderr.log" &
154+ # Bidirectional pipes: fd3 = server stdin (write), fd4 = server stdout (read)
155+ SERVER_IN=$( mktemp -u) .in
156+ SERVER_OUT=$( mktemp -u) .out
157+ mkfifo " $SERVER_IN " " $SERVER_OUT "
158+
159+ CBM_DIAGNOSTICS=1 " $BINARY " < " $SERVER_IN " > " $SERVER_OUT " 2> " $RESULTS_DIR /server-stderr.log" &
136160SERVER_PID=$!
137- SLEEP_PID=$( jobs -p | head -1)
161+
162+ # Open fds AFTER server starts (otherwise fifo blocks)
163+ exec 3> " $SERVER_IN " # write to server stdin
164+ exec 4< " $SERVER_OUT " # read from server stdout
138165sleep 3
139166
140167if ! kill -0 " $SERVER_PID " 2> /dev/null; then
141168 echo " FAIL: server did not start"
142- kill " $SLEEP_PID " 2> /dev/null || true
169+ exec 3>& - 4< & -
170+ rm -f " $SERVER_IN " " $SERVER_OUT "
143171 exit 1
144172fi
145173echo " OK: server running (pid=$SERVER_PID )"
146174
175+ # Send initialize handshake
176+ echo ' {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"capabilities":{}}}' >&3
177+ read -t 10 INIT_RESP < & 4 || true
178+
147179# ── Phase 2: Initial index ───────────────────────────────────────
148180
149181echo " --- Phase 2: initial index ---"
150- cli_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }"
182+ mcp_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }"
151183sleep 6 # wait for diagnostics write
152184collect_snapshot
153185
@@ -172,8 +204,8 @@ while [ "$(date +%s)" -lt "$END_TIME" ]; do
172204 CYCLE=$(( CYCLE + 1 ))
173205
174206 # Queries every 2 seconds
175- cli_call search_graph " {\" project\" :\" $PROJ_NAME \" ,\" name_pattern\" :\" .*compute.*\" }"
176- cli_call trace_call_path " {\" project\" :\" $PROJ_NAME \" ,\" function_name\" :\" compute\" ,\" direction\" :\" both\" }"
207+ mcp_call search_graph " {\" project\" :\" $PROJ_NAME \" ,\" name_pattern\" :\" .*compute.*\" }"
208+ mcp_call trace_call_path " {\" project\" :\" $PROJ_NAME \" ,\" function_name\" :\" compute\" ,\" direction\" :\" both\" }"
177209
178210 # File mutation every 2 minutes
179211 if [ $(( NOW - LAST_MUTATE)) -ge 120 ]; then
@@ -185,7 +217,7 @@ while [ "$(date +%s)" -lt "$END_TIME" ]; do
185217
186218 # Full reindex every 5 minutes
187219 if [ $(( NOW - LAST_REINDEX)) -ge 300 ]; then
188- cli_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }"
220+ mcp_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }"
189221 LAST_REINDEX=$NOW
190222 fi
191223
@@ -212,32 +244,42 @@ echo "OK: idle CPU=${IDLE_CPU}%"
212244if [ " $SKIP_CRASH " != " --skip-crash-test" ]; then
213245 echo " --- Phase 5: crash recovery ---"
214246
215- # Start a reindex then kill -9
216- cli_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }" &
217- INDEX_PID=$!
218- sleep 1
219- kill -9 " $INDEX_PID " 2> /dev/null || true
220- wait " $INDEX_PID " 2> /dev/null || true
247+ # Kill server mid-operation, restart, verify clean index
248+ mcp_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }"
249+ kill -9 " $SERVER_PID " 2> /dev/null || true
250+ wait " $SERVER_PID " 2> /dev/null || true
251+ exec 3>& - 4< & -
252+
253+ # Restart server
254+ CBM_DIAGNOSTICS=1 " $BINARY " < " $SERVER_IN " > " $SERVER_OUT " 2>> " $RESULTS_DIR /server-stderr.log" &
255+ SERVER_PID=$!
256+ exec 3> " $SERVER_IN "
257+ exec 4< " $SERVER_OUT "
258+ sleep 3
221259
222- # Verify the server is still alive (kill was on CLI, not server)
223260 if kill -0 " $SERVER_PID " 2> /dev/null; then
224- echo " OK: server survived crash test"
261+ echo " OK: server restarted after kill -9"
262+ echo ' {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"capabilities":{}}}' >&3
263+ read -t 10 INIT_RESP < & 4 || true
225264
226- # Verify clean re-index after crash
227- cli_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }"
228- echo " OK: clean re-index after crash"
265+ # Verify clean re-index works
266+ mcp_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }"
267+ echo " OK: clean re-index after crash recovery "
229268 else
230- echo " FAIL: server died during crash test"
269+ echo " FAIL: server did not restart after kill -9"
270+ PASS=false
231271 fi
232272fi
233273
234274# ── Phase 6: Shutdown + analysis ─────────────────────────────────
235275
236276echo " --- Phase 6: shutdown + analysis ---"
237- kill " $SLEEP_PID " 2> /dev/null || true # close stdin → EOF → server exits
277+ exec 3>& - # close server stdin → EOF → clean exit
238278sleep 2
279+ exec 4< & - # close stdout reader
239280kill " $SERVER_PID " 2> /dev/null || true
240281wait " $SERVER_PID " 2> /dev/null || true
282+ rm -f " $SERVER_IN " " $SERVER_OUT "
241283
242284# Final diagnostics (written by thread before exit)
243285FINAL_DIAG=" /tmp/cbm-diagnostics-${SERVER_PID} .json"
0 commit comments