synapse-hub 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.agents/agents/orchestrator.md +7 -0
  2. package/.agents/rules/lessons-learned.md +1 -0
  3. package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +1 -1
  4. package/.agents/skills/antigravity-test-sentinel/SKILL.md +1 -2
  5. package/.agents/skills/api-network-mocking-interception/SKILL.md +1 -1
  6. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +11 -4
  7. package/.agents/skills/architecture-decision-records/SKILL.md +1 -1
  8. package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +1 -1
  9. package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +32 -11
  10. package/.agents/skills/bats-testing-patterns/SKILL.md +1 -1
  11. package/.agents/skills/clean-pom-crafter/SKILL.md +1 -4
  12. package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +1 -1
  13. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +1 -1
  14. package/.agents/skills/e2e-testing-patterns/SKILL.md +1 -1
  15. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +1 -3
  16. package/.agents/skills/file-and-report-validator/SKILL.md +1 -1
  17. package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +1 -1
  18. package/.agents/skills/github-actions-templates/SKILL.md +1 -1
  19. package/.agents/skills/graphify-impact-analyzer/SKILL.md +1 -3
  20. package/.agents/skills/katalon-cli-runner/SKILL.md +1 -1
  21. package/.agents/skills/lam-script-generator/SKILL.md +1 -2
  22. package/.agents/skills/playwright-live-interaction/SKILL.md +1 -4
  23. package/.agents/skills/security-dast-sentinel/SKILL.md +1 -1
  24. package/.agents/skills/self-healing-locator/SKILL.md +1 -2
  25. package/.agents/skills/skillspector-security-guard/SKILL.md +115 -0
  26. package/.agents/skills/synthetic-test-data-generator/SKILL.md +1 -1
  27. package/.agents/skills/token-and-context-optimizer/SKILL.md +1 -1
  28. package/.agents/skills/visual-regression-sentinel/SKILL.md +1 -1
  29. package/.agents/skills/wcag-audit-patterns/SKILL.md +1 -1
  30. package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +1 -1
  31. package/AGENTS.md +36 -0
  32. package/README.md +140 -103
  33. package/package.json +1 -1
  34. package/skills_manifest.json +42 -29
  35. package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +15 -1
  36. package/templates/ai_bridges/CLAUDE.md +31 -0
  37. package/templates/ai_bridges/copilot-instructions.md +25 -0
  38. package/templates/ai_bridges/cursor_synapse_qa.mdc +26 -0
  39. package/templates/ai_bridges/windsurfrules +25 -0
  40. package/templates/ci/github-actions-synapse.yml +28 -0
  41. package/templates/ci/gitlab-ci-synapse.yml +16 -0
  42. package/tools/TOOLS_RUNBOOK.md +203 -12
  43. package/tools/browser_helpers.js +2 -2
  44. package/tools/bump_version.py +8 -0
  45. package/tools/cli.py +74 -2
  46. package/tools/export_postman_collection.py +535 -218
  47. package/tools/generate_jmx_plan.py +672 -102
  48. package/tools/git_pre_commit_guard.py +21 -0
  49. package/tools/jtl_analyzer.py +474 -0
  50. package/tools/mcp_excel_server.ps1 +1 -1
  51. package/tools/mcp_server.py +1 -1
  52. package/tools/newman_analyzer.py +385 -0
  53. package/tools/setup_project.py +203 -18
  54. package/tools/skillspector.py +1141 -0
  55. package/tools/telemetry_correlator.py +351 -0
  56. package/tools/templates/jmeter-distributed/README.md +54 -0
  57. package/tools/templates/jmeter-distributed/docker-compose.yml +83 -0
  58. package/tools/update_sentinel.py +313 -0
@@ -0,0 +1,351 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ tools/telemetry_correlator.py — Full-Stack APM & Server Telemetry Performance Correlator
5
+ ---------------------------------------------------------------------------------------
6
+ Correlates Client-Side perceived response times (from JMeter .jtl) with Server-Side
7
+ infrastructure metrics (Prometheus Node Exporter, PerfMon, CloudWatch, Datadog),
8
+ performs Pearson correlation analysis, isolates resource bottlenecks, and generates
9
+ an automated Root Cause Analysis (RCA) diagnostic report for Senior Performance QCs.
10
+
11
+ Capabilities:
12
+ 1. Multi-Dimensional Correlation:
13
+ - Client Latency (P95/P99 ms) vs Server CPU Utilization (%)
14
+ - Client Latency vs Server Memory Utilization (%) / JVM GC Pauses
15
+ - Client Latency vs Database Connection Pool Saturation
16
+ - Client Latency vs Network I/O & Disk Write IOPS
17
+ 2. Root Cause Attribution Engine:
18
+ - Detects CPU saturation (CPU > 85% correlating with latency spikes)
19
+ - Detects Memory leak / JVM Stop-the-world GC pauses (RAM > 90%)
20
+ - Detects Database connection starvation (DB Pool = Max)
21
+ - Detects Network / Gateway / Downstream third-party bottleneck
22
+ 3. CI/CD Artifacts:
23
+ - Visual ANSI Correlation Matrix
24
+ - GitHub / GitLab Markdown RCA report with actionable tuning recommendations
25
+ - Structured JSON diagnostic export.
26
+
27
+ Usage:
28
+ python tools/telemetry_correlator.py --jtl results.jtl --server-metrics server.csv
29
+ python tools/telemetry_correlator.py --sample --markdown rca_report.md
30
+ agent-qa correlate-telemetry --jtl results.jtl --server-metrics server.csv
31
+ """
32
+
33
+ import os
34
+ import sys
35
+ import csv
36
+ import json
37
+ import math
38
+ import argparse
39
+ from typing import Dict, List, Any, Optional, Tuple
40
+
41
+ # Enforce UTF-8 stdout encoding for Windows/macOS/Linux console compatibility
42
+ if hasattr(sys.stdout, "reconfigure"):
43
+ try:
44
+ sys.stdout.reconfigure(encoding="utf-8")
45
+ sys.stderr.reconfigure(encoding="utf-8")
46
+ except Exception:
47
+ pass
48
+
49
+
50
+ def calculate_pearson_correlation(x: List[float], y: List[float]) -> float:
51
+ """Calculates Pearson correlation coefficient between two numeric series."""
52
+ n = min(len(x), len(y))
53
+ if n < 2:
54
+ return 0.0
55
+
56
+ x = x[:n]
57
+ y = y[:n]
58
+
59
+ mean_x = sum(x) / n
60
+ mean_y = sum(y) / n
61
+
62
+ numerator = sum((x[i] - mean_x) * (y[i] - mean_y) for i in range(n))
63
+ denom_x = sum((x[i] - mean_x) ** 2 for i in range(n))
64
+ denom_y = sum((y[i] - mean_y) ** 2 for i in range(n))
65
+
66
+ if denom_x <= 0 or denom_y <= 0:
67
+ return 0.0
68
+
69
+ return numerator / math.sqrt(denom_x * denom_y)
70
+
71
+
72
+ def parse_jtl_time_series(jtl_path: str, bucket_sec: int = 5) -> Dict[int, List[float]]:
73
+ """Buckets JMeter elapsed times by N-second time windows."""
74
+ buckets: Dict[int, List[float]] = {}
75
+ with open(jtl_path, "r", encoding="utf-8", errors="replace") as f:
76
+ reader = csv.DictReader(f)
77
+ for row in reader:
78
+ try:
79
+ ts_ms = int(float(row.get("timeStamp") or row.get("Timestamp") or 0))
80
+ elapsed = float(row.get("elapsed") or row.get("Elapsed") or 0)
81
+ sec_bucket = (ts_ms // 1000 // bucket_sec) * bucket_sec
82
+ if sec_bucket not in buckets:
83
+ buckets[sec_bucket] = []
84
+ buckets[sec_bucket].append(elapsed)
85
+ except Exception:
86
+ continue
87
+ return buckets
88
+
89
+
90
+ def parse_server_metrics(metrics_path: str, bucket_sec: int = 5) -> Dict[str, Dict[int, float]]:
91
+ """
92
+ Parses server telemetry CSV (timestamp/time, cpu_pct, memory_pct, db_pool_pct, disk_iops).
93
+ """
94
+ series: Dict[str, Dict[int, float]] = {
95
+ "cpu_pct": {},
96
+ "memory_pct": {},
97
+ "db_pool_pct": {},
98
+ "disk_iops": {}
99
+ }
100
+
101
+ with open(metrics_path, "r", encoding="utf-8", errors="replace") as f:
102
+ reader = csv.DictReader(f)
103
+ for row in reader:
104
+ try:
105
+ # Timestamp can be unix epoch in seconds or milliseconds
106
+ raw_ts = float(row.get("timestamp") or row.get("time") or row.get("epoch") or 0)
107
+ ts_sec = int(raw_ts if raw_ts < 1e11 else raw_ts / 1000)
108
+ bucket = (ts_sec // bucket_sec) * bucket_sec
109
+
110
+ if "cpu" in row or "cpu_pct" in row:
111
+ series["cpu_pct"][bucket] = float(row.get("cpu_pct") or row.get("cpu") or 0)
112
+ if "memory" in row or "memory_pct" in row or "mem_pct" in row:
113
+ series["memory_pct"][bucket] = float(row.get("memory_pct") or row.get("memory") or 0)
114
+ if "db_pool" in row or "db_pool_pct" in row or "db_connections" in row:
115
+ series["db_pool_pct"][bucket] = float(row.get("db_pool_pct") or row.get("db_pool") or 0)
116
+ if "disk_iops" in row or "disk" in row or "iops" in row:
117
+ series["disk_iops"][bucket] = float(row.get("disk_iops") or row.get("disk") or 0)
118
+ except Exception:
119
+ continue
120
+
121
+ return series
122
+
123
+
124
+ def run_correlation_analysis(
125
+ jtl_buckets: Dict[int, List[float]],
126
+ server_series: Dict[str, Dict[int, float]]
127
+ ) -> Dict[str, Any]:
128
+ """Aligns time series and evaluates Pearson correlation & Root Cause."""
129
+ sorted_buckets = sorted(jtl_buckets.keys())
130
+ if not sorted_buckets:
131
+ return {"status": "NO_DATA", "correlations": {}, "root_causes": []}
132
+
133
+ # Calculate P95 per bucket
134
+ p95_series: List[float] = []
135
+ aligned_cpu: List[float] = []
136
+ aligned_mem: List[float] = []
137
+ aligned_db: List[float] = []
138
+ aligned_disk: List[float] = []
139
+
140
+ for b in sorted_buckets:
141
+ values = sorted(jtl_buckets[b])
142
+ p95 = values[int(len(values) * 0.95)] if values else 0.0
143
+ p95_series.append(p95)
144
+
145
+ aligned_cpu.append(server_series["cpu_pct"].get(b, 0.0))
146
+ aligned_mem.append(server_series["memory_pct"].get(b, 0.0))
147
+ aligned_db.append(server_series["db_pool_pct"].get(b, 0.0))
148
+ aligned_disk.append(server_series["disk_iops"].get(b, 0.0))
149
+
150
+ corr_cpu = calculate_pearson_correlation(p95_series, aligned_cpu)
151
+ corr_mem = calculate_pearson_correlation(p95_series, aligned_mem)
152
+ corr_db = calculate_pearson_correlation(p95_series, aligned_db)
153
+ corr_disk = calculate_pearson_correlation(p95_series, aligned_disk)
154
+
155
+ max_cpu = max(aligned_cpu) if aligned_cpu else 0
156
+ max_mem = max(aligned_mem) if aligned_mem else 0
157
+ max_db = max(aligned_db) if aligned_db else 0
158
+ max_p95 = max(p95_series) if p95_series else 0
159
+
160
+ root_causes: List[Dict[str, str]] = []
161
+
162
+ if corr_cpu >= 0.65 and max_cpu > 80:
163
+ root_causes.append({
164
+ "component": "Server CPU Compute",
165
+ "severity": "CRITICAL",
166
+ "correlation": f"{corr_cpu:.2f}",
167
+ "description": f"Strong positive correlation ({corr_cpu:.2f}) with peak CPU reaching {max_cpu:.1f}%. Compute capacity is saturated under peak load.",
168
+ "recommendation": "Profile hot methods/threads with async-profiler. Consider CPU thread pool tuning or vertical/horizontal pod autoscaling (HPA)."
169
+ })
170
+
171
+ if corr_db >= 0.65 and max_db > 85:
172
+ root_causes.append({
173
+ "component": "Database Connection Pool",
174
+ "severity": "CRITICAL",
175
+ "correlation": f"{corr_db:.2f}",
176
+ "description": f"Strong positive correlation ({corr_db:.2f}) with DB Connection Pool reaching {max_db:.1f}% capacity. Threads are blocked waiting for pooled DB connections.",
177
+ "recommendation": "Increase HikariCP / Tomcat maxPoolSize. Audit slow SQL queries holding connections open or enable connection leak detection."
178
+ })
179
+
180
+ if corr_mem >= 0.60 and max_mem > 85:
181
+ root_causes.append({
182
+ "component": "Server Memory / JVM Heap",
183
+ "severity": "HIGH",
184
+ "correlation": f"{corr_mem:.2f}",
185
+ "description": f"Memory saturation reached {max_mem:.1f}% with correlation ({corr_mem:.2f}). Probable GC pause (Stop-The-World) throttling throughput.",
186
+ "recommendation": "Inspect GC logs (-Xlog:gc*). Tune JVM garbage collection flags (G1GC / ZGC) or adjust heap headroom (-Xmx)."
187
+ })
188
+
189
+ if not root_causes:
190
+ if max_p95 > 1000 and max_cpu < 50 and max_db < 50:
191
+ root_causes.append({
192
+ "component": "Downstream Network / External Dependency",
193
+ "severity": "MEDIUM",
194
+ "correlation": "N/A",
195
+ "description": f"Latency spiked to {max_p95:.1f}ms while Server CPU ({max_cpu:.1f}%) and DB Pool ({max_db:.1f}%) remained relaxed.",
196
+ "recommendation": "Audit external 3rd-party HTTP gateways, microservice RPC timeouts, or lock contention in synchronized code blocks."
197
+ })
198
+ else:
199
+ root_causes.append({
200
+ "component": "System Health Normal",
201
+ "severity": "INFO",
202
+ "correlation": "N/A",
203
+ "description": "Server hardware resources scale stably within acceptable performance boundaries.",
204
+ "recommendation": "Maintain current infrastructure capacity parameters."
205
+ })
206
+
207
+ return {
208
+ "status": "COMPLETED",
209
+ "sample_points": len(sorted_buckets),
210
+ "metrics_summary": {
211
+ "max_p95_ms": round(max_p95, 2),
212
+ "max_cpu_pct": round(max_cpu, 2),
213
+ "max_memory_pct": round(max_mem, 2),
214
+ "max_db_pool_pct": round(max_db, 2),
215
+ },
216
+ "correlations": {
217
+ "cpu_correlation": round(corr_cpu, 3),
218
+ "memory_correlation": round(corr_mem, 3),
219
+ "db_pool_correlation": round(corr_db, 3),
220
+ "disk_correlation": round(corr_disk, 3)
221
+ },
222
+ "root_causes": root_causes
223
+ }
224
+
225
+
226
+ def format_rca_markdown(analysis: Dict[str, Any]) -> str:
227
+ """Generates GitHub / GitLab Markdown RCA report."""
228
+ summary = analysis["metrics_summary"]
229
+ corrs = analysis["correlations"]
230
+ causes = analysis["root_causes"]
231
+
232
+ lines = [
233
+ "# 🔬 Full-Stack Performance Telemetry Correlation & RCA Report",
234
+ "",
235
+ "### 📊 Telemetry Metrics Summary",
236
+ f"- **Max Client P95 Latency:** `{summary['max_p95_ms']} ms`",
237
+ f"- **Peak Server CPU Utilization:** `{summary['max_cpu_pct']}%`",
238
+ f"- **Peak Memory Consumption:** `{summary['max_memory_pct']}%`",
239
+ f"- **Peak Database Pool Usage:** `{summary['max_db_pool_pct']}%`",
240
+ "",
241
+ "### 🔗 Pearson Correlation Matrix (Client Latency vs Server Telemetry)",
242
+ "| Server Metric | Pearson Coefficient ($r$) | Impact Level | Interpretation |",
243
+ "| :--- | :---: | :---: | :--- |",
244
+ f"| **CPU Utilization** | `{corrs['cpu_correlation']}` | {'🔴 HIGH' if corrs['cpu_correlation'] > 0.6 else '🟢 LOW'} | Latency tracks CPU load directly |",
245
+ f"| **Database Pool** | `{corrs['db_pool_correlation']}` | {'🔴 HIGH' if corrs['db_pool_correlation'] > 0.6 else '🟢 LOW'} | Connection pool saturation bottleneck |",
246
+ f"| **Memory / JVM** | `{corrs['memory_correlation']}` | {'🟠 MEDIUM' if corrs['memory_correlation'] > 0.5 else '🟢 LOW'} | Possible GC pause or memory pressure |",
247
+ f"| **Disk I/O** | `{corrs['disk_correlation']}` | {'🟠 MEDIUM' if corrs['disk_correlation'] > 0.5 else '🟢 LOW'} | Storage latency / write queueing |",
248
+ "",
249
+ "### 🎯 Root Cause Attribution & Engineering Action Items",
250
+ ""
251
+ ]
252
+
253
+ for c in causes:
254
+ badge = "🔴 CRITICAL" if c["severity"] == "CRITICAL" else ("🟠 HIGH" if c["severity"] == "HIGH" else "🔵 INFO")
255
+ lines.extend([
256
+ f"#### [{badge}] {c['component']}",
257
+ f"- **Observation:** {c['description']}",
258
+ f"- **Actionable Tuning:** {c['recommendation']}",
259
+ ""
260
+ ])
261
+
262
+ lines.extend([
263
+ "---",
264
+ "*Report auto-generated by Synapse-Hub `telemetry_correlator.py` - Enterprise Observability Suite.*"
265
+ ])
266
+ return "\n".join(lines)
267
+
268
+
269
+ def main():
270
+ parser = argparse.ArgumentParser(
271
+ description="Full-Stack APM & Server Telemetry Performance Correlator (Senior QC Performance Suite)."
272
+ )
273
+ parser.add_argument("--jtl", help="Path to JMeter results file (.jtl).")
274
+ parser.add_argument("--server-metrics", "-s", help="Path to Server Telemetry CSV (timestamp, cpu_pct, memory_pct, db_pool_pct).")
275
+ parser.add_argument("--markdown", "-m", help="Path to export Markdown RCA report.")
276
+ parser.add_argument("--json", "-j", help="Path to export structured JSON diagnostics.")
277
+ parser.add_argument("--sample", action="store_true", help="Generate synthetic client & server telemetry dataset for verification.")
278
+
279
+ args = parser.parse_args()
280
+
281
+ if args.sample or not args.jtl or not args.server_metrics:
282
+ import tempfile, time
283
+ temp_dir = tempfile.gettempdir()
284
+ temp_jtl = os.path.join(temp_dir, "sample_corr_jmeter.jtl")
285
+ temp_srv = os.path.join(temp_dir, "sample_corr_server.csv")
286
+
287
+ base_ts = int(time.time() * 1000)
288
+ # Create synthetic correlated dataset: CPU and Latency spike together in the middle
289
+ with open(temp_jtl, "w", encoding="utf-8", newline="") as f:
290
+ w = csv.writer(f)
291
+ w.writerow(["timeStamp", "elapsed", "label", "responseCode", "success"])
292
+ for sec in range(60):
293
+ # Spike between sec 20 and 40
294
+ is_spike = 20 <= sec <= 40
295
+ elapsed = 150 + (1200 if is_spike else 0) + (sec % 10)
296
+ for _ in range(10):
297
+ w.writerow([base_ts + (sec * 1000), elapsed, "[POST] /api/v1/checkout", "200", "true"])
298
+
299
+ with open(temp_srv, "w", encoding="utf-8", newline="") as f:
300
+ w = csv.writer(f)
301
+ w.writerow(["timestamp", "cpu_pct", "memory_pct", "db_pool_pct", "disk_iops"])
302
+ for sec in range(60):
303
+ is_spike = 20 <= sec <= 40
304
+ cpu = 25.0 + (65.0 if is_spike else 5.0) + (sec % 3)
305
+ mem = 60.0 + (sec * 0.2)
306
+ db = 30.0 + (60.0 if is_spike else 0.0)
307
+ w.writerow([int(base_ts / 1000) + sec, cpu, mem, db, 120])
308
+
309
+ args.jtl = temp_jtl
310
+ args.server_metrics = temp_srv
311
+ print(f"🔬 [SAMPLE MODE] Generated synthetic correlated dataset in {temp_dir}")
312
+
313
+ jtl_buckets = parse_jtl_time_series(args.jtl, bucket_sec=5)
314
+ server_series = parse_server_metrics(args.server_metrics, bucket_sec=5)
315
+
316
+ analysis = run_correlation_analysis(jtl_buckets, server_series)
317
+
318
+ print("\n" + "=" * 80)
319
+ print(" FULL-STACK APM & SERVER TELEMETRY CORRELATION ENGINE (RCA MATRIX)")
320
+ print("=" * 80)
321
+ print(f"Sample Windows Evaluated : {analysis['sample_points']} time slices (5s interval)")
322
+ print(f"Peak Client P95 Latency : {analysis['metrics_summary']['max_p95_ms']} ms")
323
+ print(f"Peak Server CPU : {analysis['metrics_summary']['max_cpu_pct']}%")
324
+ print(f"Peak Database Pool : {analysis['metrics_summary']['max_db_pool_pct']}%")
325
+ print("-" * 80)
326
+ print("Pearson Correlation Coefficients (r):")
327
+ for k, v in analysis["correlations"].items():
328
+ print(f" • {k.replace('_', ' ').title():<25} : {v:+0.3f}")
329
+ print("-" * 80)
330
+ print("🎯 Root Cause Analysis (RCA):")
331
+ for c in analysis["root_causes"]:
332
+ print(f" [{c['severity']}] {c['component']}: {c['description']}")
333
+ print(f" 👉 Recommendation: {c['recommendation']}")
334
+ print("=" * 80 + "\n")
335
+
336
+ if args.markdown:
337
+ md = format_rca_markdown(analysis)
338
+ os.makedirs(os.path.dirname(os.path.abspath(args.markdown)) or ".", exist_ok=True)
339
+ with open(args.markdown, "w", encoding="utf-8") as f:
340
+ f.write(md)
341
+ print(f"📄 Markdown RCA Report exported: {args.markdown}")
342
+
343
+ if args.json:
344
+ os.makedirs(os.path.dirname(os.path.abspath(args.json)) or ".", exist_ok=True)
345
+ with open(args.json, "w", encoding="utf-8") as f:
346
+ json.dump(analysis, f, indent=2)
347
+ print(f"📊 JSON Diagnostics exported: {args.json}")
348
+
349
+
350
+ if __name__ == "__main__":
351
+ main()
@@ -0,0 +1,54 @@
1
+ # Enterprise Apache JMeter Distributed Load Testing Architecture
2
+
3
+ This directory provides a 1-click Dockerized distributed JMeter cluster for injecting high-concurrency loads (> 5,000–50,000 VUs) across multiple injection nodes.
4
+
5
+ ---
6
+
7
+ ## Architecture Overview
8
+
9
+ ```
10
+ +-------------------+
11
+ | jmeter-master | (Controller)
12
+ +---------+---------+
13
+ | RMI (Non-SSL)
14
+ +-----------------------+-----------------------+
15
+ | | |
16
+ +-------v-------+ +-------v-------+ +-------v-------+
17
+ | jmeter-slave-1| | jmeter-slave-2| | jmeter-slave-3|
18
+ | (1,000 VUs) | | (1,000 VUs) | | (1,000 VUs) |
19
+ +-------+-------+ +-------+-------+ +-------+-------+
20
+ | | |
21
+ +-----------------------+-----------------------+
22
+ |
23
+ [Target System]
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Quick Start (3 Steps)
29
+
30
+ ### 1. Start the Cluster
31
+ ```bash
32
+ docker-compose up -d
33
+ ```
34
+
35
+ ### 2. Run Distributed Headless Execution from Master
36
+ Copy your test plan (generated by Synapse-Hub `agent-qa generate-jmx`) into `./plans/perf_plan.jmx`, then trigger:
37
+ ```bash
38
+ docker exec -it jmeter-master jmeter -n \
39
+ -t /jmeter/plans/perf_plan.jmx \
40
+ -R jmeter-slave-1,jmeter-slave-2,jmeter-slave-3 \
41
+ -Dserver.rmi.ssl.disable=true \
42
+ -l /jmeter/reports/distributed_results.jtl \
43
+ -e -o /jmeter/reports/dashboard
44
+ ```
45
+
46
+ ### 3. Evaluate SLA with Synapse-Hub Quality Gate
47
+ ```bash
48
+ agent-qa analyze-jtl ./reports/distributed_results.jtl --sla-p95 500 --max-error-rate 1.0
49
+ ```
50
+
51
+ ### 4. Teardown
52
+ ```bash
53
+ docker-compose down -v
54
+ ```
@@ -0,0 +1,83 @@
1
+ version: '3.8'
2
+
3
+ # ==============================================================================
4
+ # Apache JMeter 5.x Distributed Load Testing Cluster (Master + 3 Slaves)
5
+ # Auto-provisioned by Synapse-Hub Performance Suite
6
+ # ==============================================================================
7
+
8
+ services:
9
+ # ----------------------------------------------------------------------------
10
+ # Slave Injector Node 1
11
+ # ----------------------------------------------------------------------------
12
+ jmeter-slave-1:
13
+ image: justb4/jmeter:5.5
14
+ container_name: jmeter-slave-1
15
+ command: >
16
+ -n -s
17
+ -Dserver.rmi.ssl.disable=true
18
+ -Dserver_port=1099
19
+ -Dserver.rmi.localport=50000
20
+ expose:
21
+ - "1099"
22
+ - "50000"
23
+ networks:
24
+ - jmeter-cluster
25
+
26
+ # ----------------------------------------------------------------------------
27
+ # Slave Injector Node 2
28
+ # ----------------------------------------------------------------------------
29
+ jmeter-slave-2:
30
+ image: justb4/jmeter:5.5
31
+ container_name: jmeter-slave-2
32
+ command: >
33
+ -n -s
34
+ -Dserver.rmi.ssl.disable=true
35
+ -Dserver_port=1099
36
+ -Dserver.rmi.localport=50000
37
+ expose:
38
+ - "1099"
39
+ - "50000"
40
+ networks:
41
+ - jmeter-cluster
42
+
43
+ # ----------------------------------------------------------------------------
44
+ # Slave Injector Node 3
45
+ # ----------------------------------------------------------------------------
46
+ jmeter-slave-3:
47
+ image: justb4/jmeter:5.5
48
+ container_name: jmeter-slave-3
49
+ command: >
50
+ -n -s
51
+ -Dserver.rmi.ssl.disable=true
52
+ -Dserver_port=1099
53
+ -Dserver.rmi.localport=50000
54
+ expose:
55
+ - "1099"
56
+ - "50000"
57
+ networks:
58
+ - jmeter-cluster
59
+
60
+ # ----------------------------------------------------------------------------
61
+ # Master Controller Node
62
+ # ----------------------------------------------------------------------------
63
+ jmeter-master:
64
+ image: justb4/jmeter:5.5
65
+ container_name: jmeter-master
66
+ depends_on:
67
+ - jmeter-slave-1
68
+ - jmeter-slave-2
69
+ - jmeter-slave-3
70
+ volumes:
71
+ - ./plans:/jmeter/plans
72
+ - ./reports:/jmeter/reports
73
+ environment:
74
+ - JVM_ARGS="-Xms1g -Xmx2g"
75
+ entrypoint: ["/bin/sh", "-c"]
76
+ command: >
77
+ "tail -f /dev/null"
78
+ networks:
79
+ - jmeter-cluster
80
+
81
+ networks:
82
+ jmeter-cluster:
83
+ driver: bridge