-
Notifications
You must be signed in to change notification settings - Fork 0
feat(performance-test-server): add OTel OTLP export overhead scenario #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,22 @@ | ||
| services: | ||
| server: | ||
| build: . | ||
| # OTel export scenario (port 8085) is opt-in. When OTEL_EXPORT_ENABLED=1 | ||
| # the server initializes the @connectum/otel provider with env-driven OTLP | ||
| # exporter settings and binds the extra port. All other scenarios work | ||
| # without these env vars; the provider stays uninitialized. | ||
| environment: | ||
| OTEL_EXPORT_ENABLED: "${OTEL_EXPORT_ENABLED:-0}" | ||
| OTEL_SERVICE_NAME: "${OTEL_SERVICE_NAME:-performance-test-server}" | ||
| OTEL_TRACES_EXPORTER: "${OTEL_TRACES_EXPORTER:-otlp/grpc}" | ||
| OTEL_METRICS_EXPORTER: "${OTEL_METRICS_EXPORTER:-otlp/grpc}" | ||
| OTEL_LOGS_EXPORTER: "${OTEL_LOGS_EXPORTER:-none}" | ||
| OTEL_EXPORTER_OTLP_ENDPOINT: "${OTEL_EXPORTER_OTLP_ENDPOINT:-http://otel-collector:4317}" | ||
| # BatchSpanProcessor tuning — realistic production-ish defaults | ||
| OTEL_BSP_MAX_EXPORT_BATCH_SIZE: "${OTEL_BSP_MAX_EXPORT_BATCH_SIZE:-512}" | ||
| OTEL_BSP_MAX_QUEUE_SIZE: "${OTEL_BSP_MAX_QUEUE_SIZE:-2048}" | ||
| OTEL_BSP_SCHEDULE_DELAY: "${OTEL_BSP_SCHEDULE_DELAY:-1000}" | ||
| OTEL_BSP_EXPORT_TIMEOUT: "${OTEL_BSP_EXPORT_TIMEOUT:-10000}" | ||
| healthcheck: | ||
| test: > | ||
| node -e "const h=require('node:http2'),c=h.connect('https://localhost:8080', | ||
|
|
@@ -15,6 +31,29 @@ services: | |
| retries: 10 | ||
| start_period: 5s | ||
|
|
||
| # ========================================================================= | ||
| # OpenTelemetry Collector (only started for the otel-export profile) | ||
| # ========================================================================= | ||
| # Accepts OTLP/gRPC on :4317 and OTLP/HTTP on :4318, then drops everything | ||
| # via a debug exporter. The goal is to measure export-side CPU cost, not | ||
| # backend write throughput — see otel-collector-config.yaml for rationale. | ||
| otel-collector: | ||
| image: otel/opentelemetry-collector-contrib:0.120.0 | ||
| profiles: ["otel-export"] | ||
| volumes: | ||
| - ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml:ro | ||
| command: ["--config=/etc/otelcol-contrib/config.yaml"] | ||
| # No healthcheck: the contrib image is built FROM scratch — it has no shell, | ||
| # curl, or wget — so the OTLP listeners (:4317/:4318) and the health_check | ||
| # extension (:13133) cannot be probed from inside the container, and the only | ||
| # in-image binary (`otelcol-contrib components`) exits 0 without ever opening | ||
| # a socket, which would be a false "healthy" signal. We do NOT claim to prove | ||
| # collector readiness here. Instead, k6-otel-export gates on `service_started` | ||
| # only, and correctness does not depend on the collector being up at t=0: | ||
| # the BatchSpanProcessor buffers and retries exports, and over the ~5-minute | ||
| # steady-state run any sub-second collector startup gap is negligible relative | ||
| # to total spans exported. | ||
|
|
||
| k6-interceptor-overhead: | ||
| image: grafana/k6:latest | ||
| volumes: | ||
|
|
@@ -36,3 +75,38 @@ services: | |
| server: { condition: service_healthy } | ||
| command: run /scripts/basic-load.js | ||
| profiles: ["load"] | ||
|
|
||
| # ========================================================================= | ||
| # OTel OTLP export overhead scenario (profile: otel-export) | ||
| # ========================================================================= | ||
| # Measures the p50/p95/p99 latency delta between the baseline (port 8081) | ||
| # and full-chain-with-real-OTLP-exporter (port 8085). | ||
| # Requires OTEL_EXPORT_ENABLED=1 on the server and a running otel-collector. | ||
| # | ||
| # Readiness note: the server healthcheck below probes port 8080 only. All | ||
| # ports (incl. 8085) are bound in a single Promise.all in src/index.ts, so | ||
| # 8080 being healthy means 8085 is almost certainly up too — and the k6 | ||
| # script closes the remaining gap deterministically: its setup() health-checks | ||
| # 8081 AND 8085 and aborts the run if 8085 is not yet serving. So a brief | ||
| # bind-order race cannot produce silent, partially-measured results. | ||
| k6-otel-export: | ||
| image: grafana/k6:latest | ||
| volumes: | ||
| - ./k6:/scripts | ||
| - ./k6/results:/results | ||
| environment: | ||
| PROTOCOL: https | ||
| BASE_HOST: server | ||
| BASELINE_PORT: "8081" | ||
| OTEL_EXPORT_PORT: "8085" | ||
| # k6 writes a machine-readable summary here for CI/bench tracking. | ||
| K6_OUT: "json=/results/otel-export-overhead.json" | ||
| depends_on: | ||
| server: { condition: service_healthy } | ||
| # service_started, not service_healthy: the scratch-based collector image | ||
| # cannot be health-probed (see the otel-collector comment above). The | ||
| # BatchSpanProcessor tolerates a not-yet-ready collector via buffering and | ||
| # retries, so a started container is a sufficient precondition here. | ||
| otel-collector: { condition: service_started } | ||
| command: run /scripts/otel-export-overhead.js | ||
|
Comment on lines
+104
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The server healthcheck only exercises port 8080, but this k6 service immediately requires port 8085. If 8085 is not bound yet or 🩺 Proposed direction- node -e "const h=require('node:http2'),c=h.connect('https://localhost:8080',
+ node -e "const h=require('node:http2');const ports=process.env.OTEL_EXPORT_ENABLED==='1'?[8080,8085]:[8080];
+ let pending=ports.length, failed=false;
+ for (const port of ports){const c=h.connect(`https://localhost:${port}`,
{rejectUnauthorized:false});const r=c.request({':method':'POST',
':path':'/greeter.v1.GreeterService/SayHello','content-type':'application/json',
'connect-protocol-version':'1'});r.end(JSON.stringify({name:'health'}));
- let d='';r.on('data',x=>d+=x);r.on('end',()=>{c.close();process.exit(d?0:1)});
- r.on('error',()=>{c.close();process.exit(1)});
- setTimeout(()=>{c.close();process.exit(1)},3000)"
+ let d='';r.on('data',x=>d+=x);r.on('end',()=>{c.close();failed ||= !d;if(--pending===0)process.exit(failed?1:0)});
+ r.on('error',()=>{c.close();process.exit(1)});
+ setTimeout(()=>{c.close();process.exit(1)},3000)}"🤖 Prompt for AI Agents |
||
| profiles: ["otel-export"] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Target
k6-otel-exportexplicitly to avoid running the default benchmark too.docker compose --profile otel-export upstarts all unprofiled services as well, sok6-interceptor-overheadcan run concurrently and skew this benchmark. Specify the service name.🐛 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents