@@ -35,67 +35,150 @@ DURATION_S=$((DURATION_MIN * 60))
3535
3636echo " === soak-test: binary=$BINARY duration=${DURATION_MIN} m ==="
3737
38- # ── Helper: create test project ───────────────────────── ─────────
38+ # ── Helper: generate realistic test project (~200 files) ─────────
3939
4040SOAK_PROJECT=$( mktemp -d)
41- mkdir -p " $SOAK_PROJECT /src" " $SOAK_PROJECT /lib"
4241
43- # Create a realistic small project with multiple languages
44- cat > " $SOAK_PROJECT /src/main.py" << 'PYEOF '
45- from lib.utils import compute, validate
46-
47- def process(data):
48- if validate(data):
49- return compute(data)
50- return None
51-
52- def main():
53- result = process({"key": "value"})
54- print(result)
42+ generate_project () {
43+ local root=" $1 "
44+ # Python package (80 files)
45+ for i in $( seq 1 20) ; do
46+ local pkg=" $root /src/pkg_${i} "
47+ mkdir -p " $pkg "
48+ cat > " $pkg /__init__.py" << PYEOF
49+ from .handlers import handle_${i}
50+ from .models import Model${i}
51+ PYEOF
52+ cat > " $pkg /handlers.py" << PYEOF
53+ from .models import Model${i}
54+ from .utils import validate_${i} , transform_${i}
55+
56+ def handle_${i} (request):
57+ data = Model${i} .from_request(request)
58+ if not validate_${i} (data):
59+ return {"error": "invalid"}
60+ return transform_${i} (data)
61+
62+ def process_batch_${i} (items):
63+ return [handle_${i} (item) for item in items]
64+ PYEOF
65+ cat > " $pkg /models.py" << PYEOF
66+ class Model${i} :
67+ def __init__(self, name, value):
68+ self.name = name
69+ self.value = value
70+
71+ @classmethod
72+ def from_request(cls, req):
73+ return cls(req.get("name", ""), req.get("value", 0))
74+
75+ def to_dict(self):
76+ return {"name": self.name, "value": self.value}
77+ PYEOF
78+ cat > " $pkg /utils.py" << PYEOF
79+ def validate_${i} (data):
80+ return data is not None and hasattr(data, 'name')
5581
56- if __name__ == "__main__" :
57- main()
82+ def transform_ ${i} (data) :
83+ return {"result": data.name.upper(), "score": data.value * ${i} }
5884PYEOF
85+ done
5986
60- cat > " $SOAK_PROJECT /lib/utils.py" << 'PYEOF '
61- def compute(data):
62- total = sum(len(str(v)) for v in data.values())
63- return total
87+ # Go package (40 files)
88+ mkdir -p " $root /internal/api" " $root /internal/store" " $root /cmd"
89+ for i in $( seq 1 20) ; do
90+ cat > " $root /internal/api/handler_${i} .go" << GOEOF
91+ package api
6492
65- def validate(data):
66- return isinstance(data, dict) and len(data) > 0
93+ import "fmt"
6794
68- def helper():
69- return 42
70- PYEOF
95+ func HandleRoute${i} (path string) (string, error) {
96+ result := ProcessData${i} (path)
97+ return fmt.Sprintf("route_%d: %s", ${i} , result), nil
98+ }
7199
72- cat > " $SOAK_PROJECT /src/server.go" << 'GOEOF '
73- package main
100+ func ProcessData${i} (input string) string {
101+ return fmt.Sprintf("processed_%d_%s", ${i} , input)
102+ }
103+ GOEOF
104+ cat > " $root /internal/store/repo_${i} .go" << GOEOF
105+ package store
74106
75- import "fmt"
107+ type Entity${i} struct {
108+ ID int
109+ Name string
110+ Data map[string]interface{}
111+ }
76112
77- func StartServer(port int) error {
78- fmt.Printf("listening on %d\n", port)
79- return nil
113+ func FindEntity${i} (id int) (*Entity${i} , error) {
114+ return &Entity${i} {ID: id, Name: "entity"}, nil
80115}
81116
82- func HandleRequest(path string) string {
83- return "ok: " + path
117+ func SaveEntity ${i} (e *Entity ${i} ) error {
118+ return nil
84119}
85120GOEOF
121+ done
122+
123+ # TypeScript (40 files)
124+ mkdir -p " $root /frontend/src/components" " $root /frontend/src/hooks"
125+ for i in $( seq 1 20) ; do
126+ cat > " $root /frontend/src/components/Component${i} .tsx" << TSEOF
127+ import React from 'react';
128+ import { useData${i} } from '../hooks/useData${i} ';
129+
130+ interface Props${i} { id: number; label: string; }
131+
132+ export const Component${i} : React.FC<Props${i} > = ({ id, label }) => {
133+ const { data, loading } = useData${i} (id);
134+ if (loading) return <div>Loading...</div>;
135+ return <div className="comp-${i} ">{label}: {JSON.stringify(data)}</div>;
136+ };
137+ TSEOF
138+ cat > " $root /frontend/src/hooks/useData${i} .ts" << TSEOF
139+ import { useState, useEffect } from 'react';
140+
141+ export function useData${i} (id: number) {
142+ const [data, setData] = useState(null);
143+ const [loading, setLoading] = useState(true);
144+ useEffect(() => {
145+ fetch('/api/data/${i} /' + id)
146+ .then(r => r.json())
147+ .then(d => { setData(d); setLoading(false); });
148+ }, [id]);
149+ return { data, loading };
150+ }
151+ TSEOF
152+ done
86153
87- cat > " $SOAK_PROJECT /config.yaml" << 'YAMLEOF '
154+ # Config files
155+ cat > " $root /config.yaml" << 'YAMLEOF '
88156database:
89157 host: localhost
90158 port: 5432
159+ pool_size: 10
91160server:
92161 workers: 4
162+ timeout: 30
93163YAMLEOF
164+ cat > " $root /Dockerfile" << 'DEOF '
165+ FROM python:3.11-slim
166+ WORKDIR /app
167+ COPY . .
168+ RUN pip install -r requirements.txt
169+ CMD ["python", "-m", "src.main"]
170+ DEOF
171+ }
172+
173+ echo " Generating test project (~200 files)..."
174+ generate_project " $SOAK_PROJECT "
94175
95176# Init git repo (required for watcher)
96177git -C " $SOAK_PROJECT " init -q 2> /dev/null
97178git -C " $SOAK_PROJECT " add -A 2> /dev/null
98179git -C " $SOAK_PROJECT " -c user.email=test@test -c user.name=test commit -q -m " init" 2> /dev/null
180+ FILE_COUNT=$( find " $SOAK_PROJECT " -type f | wc -l | tr -d ' ' )
181+ echo " OK: $FILE_COUNT files in test project"
99182
100183# ── Helper: run CLI tool call and record latency ─────────────────
101184
@@ -137,14 +220,14 @@ mcp_call() {
137220collect_snapshot () {
138221 local diag_file=" /tmp/cbm-diagnostics-${SERVER_PID} .json"
139222 if [ -f " $diag_file " ]; then
140- local ts= $( date +%s )
141- local uptime= $( python3 -c " import json; d=json.load(open(' $diag_file ')); print(d.get('uptime_s',0)) " 2> /dev/null || echo " 0 " )
142- local rss= $( python3 -c " import json; d=json .load(open('$diag_file ')); print(d.get('rss_bytes',0)) " 2> /dev/null || echo " 0 " )
143- local commit= $( python3 -c " import json; d=json.load(open(' $diag_file ')); print(d.get('heap_committed_bytes',0)) " 2> /dev/null || echo " 0 " )
144- local fds= $( python3 -c " import json; d=json.load(open(' $diag_file ')); print(d. get('fd_count',0)) " 2> /dev/null || echo " 0 " )
145- local qcount= $( python3 -c " import json; d=json.load(open(' $diag_file ')); print( d.get('query_count',0)) " 2> /dev/null || echo " 0 " )
146- local qmax= $( python3 -c " import json; d=json.load(open(' $diag_file ')); print( d.get('query_max_us',0)) " 2> /dev/null || echo " 0 " )
147- echo " $ts , $uptime , $rss , $commit , $fds , $qcount , $qmax " >> " $METRICS_CSV "
223+ python3 -c "
224+ import json, time
225+ d = json.load(open('$diag_file '))
226+ # Use heap_committed if available, otherwise RSS (mimalloc may report 0 for committed )
227+ mem = d. get('heap_committed_bytes', 0 )
228+ if mem == 0: mem = d.get('rss_bytes', 0 )
229+ print(f \" {int(time.time())},{d.get('uptime_s',0)},{d.get('rss_bytes',0)},{mem},{d.get('fd_count',0)},{d.get('query_count',0)},{ d.get('query_max_us',0)} \ " )
230+ " 2> /dev/null >> " $METRICS_CSV "
148231 fi
149232}
150233
@@ -215,14 +298,14 @@ while [ "$(date +%s)" -lt "$END_TIME" ]; do
215298 LAST_MUTATE=$NOW
216299 fi
217300
218- # Full reindex every 5 minutes
219- if [ $(( NOW - LAST_REINDEX)) -ge 300 ]; then
301+ # Full reindex every 2 minutes (compressed — simulates 15min real interval)
302+ if [ $(( NOW - LAST_REINDEX)) -ge 120 ]; then
220303 mcp_call index_repository " {\" repo_path\" :\" $SOAK_PROJECT \" }"
221304 LAST_REINDEX=$NOW
222305 fi
223306
224- # Collect diagnostics every 30 seconds
225- if [ $(( CYCLE % 15 )) -eq 0 ]; then
307+ # Collect diagnostics every 10 seconds (5 cycles)
308+ if [ $(( CYCLE % 5 )) -eq 0 ]; then
226309 collect_snapshot
227310 fi
228311
0 commit comments