Skip to content

feat(observability): query gateway core + dashboard shell - #5378

Closed
Ma77Ball wants to merge 4 commits into
apache:mainfrom
Ma77Ball:obs/pr4/gateway-core
Closed

Ma77Ball wants to merge 4 commits into
apache:mainfrom
Ma77Ball:obs/pr4/gateway-core

Conversation

@Ma77Ball

@Ma77Ball Ma77Ball commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Introduces the tenant-scoped read path the dashboard queries, plus the Angular shell that hosts the per-signal panels. This PR warrants the closest review: it enforces tenancy and rate limiting and is the first user-visible surface.
Backend:

  • BackendClient: HTTP client to the telemetry backends.
  • ScopeResolver: derives the caller's tenant scope and constrains every query to it.
  • RateLimiter, AuditLogger, GatewayContext: per-request rate limiting, audit logging, and shared request context.
  • dtos.scala: typed request objects with validators (time window, page size, free text, service name).
  • ObservabilityResources with the /observability/health endpoint, registered in TexeraWebApplication.
  • RequestContextMdcFilter and UserContextMdcFilter: inject request and user context into the logging MDC.
  • ObservabilityGatewayConfig and its configuration file.
    Frontend:
  • Observability dashboard page, route, and navigation entry, plus observability.service, observability.types, and the traces-pivot.service.
  • Health gating: each tab is guarded by the per-signal reachability check; an unreachable signal renders an explicit state rather than a broken panel. Signal panels follow in PR5 through PR8.

Any related issues, documentation, or discussions?

Closes: #5370
Part of #4070. Stacked on #5377.

How was this PR tested?

  • Backend specs for the gateway core, DTO validation, scope resolver, rate limiter, and MDC filters; sbt scalafmtCheckAll passes.
  • Frontend component and service specs; prettier-eslint and eslint pass.
  • Compile and the full test suites run in this PR's CI.

Was this PR authored or co-authored using generative AI tooling?

Co-authored with Claude Opus 4.8 in compliance with ASF

@github-actions github-actions Bot added engine dependencies Pull requests that update a dependency file frontend Changes related to the frontend GUI docs Changes related to documentations infra common labels Jun 5, 2026
@codecov-commenter

codecov-commenter commented Jun 5, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.84867% with 334 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.13%. Comparing base (b143a4f) to head (681c046).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
...apache/texera/web/observability/gateway/dtos.scala 41.98% 100 Missing and 5 partials ⚠️
...texera/web/observability/gateway/AuditLogger.scala 0.00% 41 Missing ⚠️
...observability/gateway/ObservabilityResources.scala 0.00% 38 Missing ⚠️
...ala/org/apache/texera/observability/OtelInit.scala 70.86% 25 Missing and 12 partials ⚠️
...xera/web/observability/gateway/ScopeResolver.scala 22.58% 22 Missing and 2 partials ⚠️
...rg/apache/texera/web/service/WorkflowService.scala 74.13% 11 Missing and 4 partials ⚠️
...xera/web/observability/gateway/BackendClient.scala 72.91% 5 Missing and 8 partials ⚠️
.../apache/texera/observability/WorkflowMetrics.scala 84.93% 4 Missing and 7 partials ⚠️
...e/texera/observability/TexeraOtelLogAppender.scala 78.04% 2 Missing and 7 partials ⚠️
...era/common/config/ObservabilityGatewayConfig.scala 0.00% 7 Missing ⚠️
... and 13 more
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5378      +/-   ##
============================================
- Coverage     92.60%   92.13%   -0.47%     
- Complexity     5043     5093      +50     
============================================
  Files          1252     1277      +25     
  Lines         53587    54516     +929     
  Branches       6673     6840     +167     
============================================
+ Hits          49623    50231     +608     
- Misses         2315     2580     +265     
- Partials       1649     1705      +56     
Flag Coverage Δ
access-control-service 77.44% <100.00%> (+0.06%) ⬆️
agent-service 99.16% <ø> (ø)
amber 87.00% <62.54%> (-1.09%) ⬇️
computing-unit-managing-service 60.52% <100.00%> (+0.03%) ⬆️
config-service 87.50% <100.00%> (+0.12%) ⬆️
file-service 81.45% <0.00%> (-0.09%) ⬇️
frontend 96.57% <93.93%> (-0.02%) ⬇️
notebook-migration-service 83.77% <100.00%> (+0.03%) ⬆️
pyamber 98.52% <ø> (-0.07%) ⬇️
workflow-compiling-service 74.25% <100.00%> (+0.15%) ⬆️

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added the platform Non-amber Scala service paths label Jun 5, 2026
@github-actions

github-actions Bot commented Jun 12, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 0 better · 🔴 7 worse · ⚪ 8 noise (<±5%) · 0 without baseline

Compared against main be65bf8 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 388 0.237 23,866/32,686/32,686 us 🔴 -13.2% / 🔴 +122.8%
⚪ bs=100 sw=10 sl=64 823 0.502 120,996/131,016/131,016 us ⚪ within ±5% / 🔴 +28.0%
🔴 bs=1000 sw=10 sl=64 928 0.566 1,080,678/1,177,038/1,177,038 us 🔴 +7.8% / 🔴 +19.6%
Baseline details

Latest main be65bf8 from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 388 tuples/sec 447 tuples/sec 866.99 tuples/sec -13.2% -55.2%
bs=10 sw=10 sl=64 MB/s 0.237 MB/s 0.273 MB/s 0.529 MB/s -13.2% -55.2%
bs=10 sw=10 sl=64 p50 23,866 us 21,380 us 11,989 us +11.6% +99.1%
bs=10 sw=10 sl=64 p95 32,686 us 30,534 us 14,672 us +7.0% +122.8%
bs=10 sw=10 sl=64 p99 32,686 us 30,534 us 17,906 us +7.0% +82.5%
bs=100 sw=10 sl=64 throughput 823 tuples/sec 850 tuples/sec 1,111 tuples/sec -3.2% -25.9%
bs=100 sw=10 sl=64 MB/s 0.502 MB/s 0.519 MB/s 0.678 MB/s -3.3% -26.0%
bs=100 sw=10 sl=64 p50 120,996 us 117,059 us 95,405 us +3.4% +26.8%
bs=100 sw=10 sl=64 p95 131,016 us 126,323 us 102,332 us +3.7% +28.0%
bs=100 sw=10 sl=64 p99 131,016 us 126,323 us 115,219 us +3.7% +13.7%
bs=1000 sw=10 sl=64 throughput 928 tuples/sec 938 tuples/sec 1,141 tuples/sec -1.1% -18.6%
bs=1000 sw=10 sl=64 MB/s 0.566 MB/s 0.573 MB/s 0.696 MB/s -1.2% -18.7%
bs=1000 sw=10 sl=64 p50 1,080,678 us 1,066,908 us 938,794 us +1.3% +15.1%
bs=1000 sw=10 sl=64 p95 1,177,038 us 1,091,841 us 984,521 us +7.8% +19.6%
bs=1000 sw=10 sl=64 p99 1,177,038 us 1,091,841 us 1,008,827 us +7.8% +16.7%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,515.45,200,128000,388,0.237,23865.86,32685.87,32685.87
1,100,10,64,20,2429.27,2000,1280000,823,0.502,120996.38,131015.84,131015.84
2,1000,10,64,20,21555.49,20000,12800000,928,0.566,1080678.43,1177038.46,1177038.46

@github-actions

github-actions Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Committers with relevant context: @parshimers
    You can request their reviews formally with /request-review @parshimers.

  • Contributors with relevant context: @bobbai00, @dzueck, @aicam
    You can notify them by mentioning @bobbai00, @dzueck, @aicam in a comment.

Introduce the OpenTelemetry foundation for Texera: an SDK bootstrap, a
Logback-to-OTel log bridge, and a log sanitizer, wired into every service
entry point. This is PR 1 of the observability stack; it ships logging only,
with the trace and metric exporters wired but not yet emitted (those arrive in
follow-up PRs).

New module (common/observability):
- OtelInit: one-call SDK bootstrap per service. Reads OTEL_* settings from
  observability.conf, validates the OTLP endpoint (scheme + host allowlist)
  before any exporter is built, wires the span/log/metric providers explicitly
  (no sdk-extension-autoconfigure), clamps the metric export interval, and
  attaches the log appender to the Logback ROOT logger. The whole init body is
  guarded so a missing or malformed config returns None with one WARN instead
  of throwing into a service's run(), even when telemetry is disabled.
- TexeraOtelLogAppender: bridges Logback events to OTel log records. Maps
  severity, forwards MDC, sets exception.type/message/stacktrace semantic
  attributes, and drops records from io.opentelemetry loggers so export-failure
  diagnostics are not fed back to the collector that just failed.
- LogSanitizer: strips C0 control characters, redacts secrets, and caps body
  length (MaxBodyChars) to keep individual records bounded.

Config (common/config):
- observability.conf with the OTEL_* defaults, ObservabilityConfig to read it,
  and ENV_OTEL_* entries in EnvironmentalVariable.

Wiring:
- build.sbt defines the Observability module and adds dependsOn(Observability)
  to the eight Dropwizard services.
- Each service entry point calls OtelInit.init with its own service name, and
  each service config gains a logging block.

Deployment:
- OTEL_* env entries in bin/single-node/.env, bin/k8s/values.yaml, and
  bin/k8s/values-development.yaml (kept a name-for-name mirror).
- LICENSE-binary manifests updated with the pinned OTel 1.50.0 jars.

Tests: OtelInitSpec, TexeraOtelLogAppenderSpec, LogSanitizerSpec, and
ObservabilityConfigSpec cover endpoint validation, interval clamping,
severity/MDC/exception mapping, self-diagnostic filtering, redaction, and
truncation.

Rebased onto current main.
Build on the logging foundation (PR 1) with the backend emit path: workflow
lifecycle metrics and a run-level setup trace span, plus the tracing/metrics
primitives they use. This is PR 2 of the observability stack.

Primitives (common/observability):
- TexeraTracer: lazy accessor for the process tracer off GlobalOpenTelemetry.
- SpanAttrs: typed AttributeKey constants so span attributes use standard keys
  rather than ad hoc strings.
- WorkflowMetrics: the OTel instruments (start/completion/failure/cancellation
  counters and run-duration histogram) keyed by workflow kind.
- TraceparentValidator: validates W3C traceparent headers before use.

Emit path (amber):
- WorkflowMetricsRecorder: single owner of the metric instruments. init() wires
  them once; onStart stamps a run's start; onStateChange records terminal
  counters and duration exactly once on the first transition into a terminal
  state (idempotent, safe to call on every transition).
- WorkflowService.initExecutionService runs inside a run-level setup span so
  setup-path logs carry its trace id. The span covers only the synchronous
  setup; synchronous setup failures are recorded on it in the catch block, and
  the span is ended in finally. The async errorHandler deliberately does not
  touch the span: it is invoked after setup returns and the span has ended, so
  the failure is surfaced through the metadata store instead.
- ExecutionStateStore.updateWorkflowState is the single chokepoint that feeds
  every state transition to WorkflowMetricsRecorder.onStateChange.
- ComputingUnitMaster initializes the recorder at startup.

All observability sources live under common/observability (the module from
PR 1); the tracing/metrics classes are not duplicated into common/config.

Tests: WorkflowMetricsSpec, SpanAttrsSpec, and TraceparentValidatorSpec.

Review follow-ups addressed: span attributes use SpanAttrs keys instead of
plain strings; the error handler no longer records onto a span that may have
already ended (documented and handled via the metadata store).

Stacked on PR 1 (obs/pr1/foundations).
@Ma77Ball
Ma77Ball force-pushed the obs/pr4/gateway-core branch from 6852c5c to dc4490e Compare October 9, 2026 18:30
Add the local deployment wiring that receives and stores the telemetry
emitted by PR 2: an OpenTelemetry Collector plus logs, metrics, traces,
and profiles backends, for both the single-node docker-compose stack and
the Kubernetes Helm chart. Infrastructure only; the application runs
unchanged whether or not these services are started. This is PR 3 of the
observability stack.

Single-node compose (bin/single-node, bin/observability):
- otel-collector as the single OTLP ingress, routing to VictoriaLogs
  (logs), VictoriaMetrics (metrics), and Jaeger (traces); the collector
  config caps OTLP message size and bounds memory via memory_limiter.
- Parca server + eBPF agent for continuous profiling, configured in
  bin/observability/parca.
- Each signal is its own compose profile and is OFF by default:
  COMPOSE_PROFILES is empty, so observability is opt-in. docker compose
  reads COMPOSE_PROFILES natively via the canonical launcher.
- Emission is gated by OTEL_SDK_DISABLED (default disabled); nothing is
  emitted unless an operator opts in alongside the collector profile.

Kubernetes Helm chart (bin/k8s):
- Templates for the collector, Jaeger, VictoriaLogs, and VictoriaMetrics
  under templates/base/observability, plus values.yaml knobs and an
  install.sh helper.
- Service deployments gain the OTLP endpoint wiring.

Every observability port binds to loopback (127.0.0.1) or stays on the
bridge network; no 0.0.0.0 host bindings.

Tests: ObservabilityComposeSpec and ParcaConfigSpec (string-level smoke
tests over the compose, collector, and Parca config, pinning image
versions, the opt-in default, loopback binding, and the no-high-
cardinality-label rule). All 24 pass; sbt scalafmtCheckAll is clean.

Review follow-ups addressed: observability is opt-in rather than on by
default, and the privileged eBPF agent is opt-in with its host-wide scope
documented; removed the non-canonical up.sh launcher so COMPOSE_PROFILES
is the single switch; corrected the Parca README (the agent profiles every
host process, and profiles cannot be joined to traces by trace_id); moved
the local-dev-only OTLP host ports into docker-compose.override.yml so the
base compose stays deploy-clean; the release workflow now ships
bin/observability and rewrites the mount paths so the compose works from
the released single-node bundle; and removed the inter-PR references from
the configs, comments, and specs.

Stacked on PR 2 (obs/pr2/backend-emit).
@Ma77Ball
Ma77Ball force-pushed the obs/pr4/gateway-core branch from dc4490e to 5e21599 Compare October 9, 2026 18:42
@Ma77Ball
Ma77Ball marked this pull request as ready for review October 9, 2026 18:43
@Ma77Ball

Ma77Ball commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

/request-review @zuozhiw

@github-actions
github-actions Bot requested a review from zuozhiw October 9, 2026 18:58
@Ma77Ball
Ma77Ball force-pushed the obs/pr4/gateway-core branch from 5e21599 to 907d120 Compare October 10, 2026 06:40
Add the tenant-scoped read path the dashboard queries, plus the Angular
shell that hosts the per-signal panels. This is the first user-visible
observability surface: it enforces tenancy and rate limiting on every
query and gates each panel on signal reachability. This is PR 4 of the
observability stack.

Query gateway (amber, org.apache.texera.web.observability.gateway):
- BackendClient issues the read-only HTTP queries to the telemetry
  backends (VictoriaLogs, VictoriaMetrics, Jaeger).
- ScopeResolver derives the caller's tenant scope and constrains every
  query to it, so a user never reads another tenant's telemetry.
- RateLimiter, AuditLogger, and GatewayContext add per-request rate
  limiting, audit logging, and the shared request context.
- dtos.scala and builders.scala define the typed request objects and
  their validators (time window, page size, free text, service name).
- ObservabilityResources exposes /observability/health and is
  registered in TexeraWebApplication.
- RequestContextMdcFilter and UserContextMdcFilter inject the request
  and user context into the logging MDC.
- ObservabilityGatewayConfig plus observability-gateway.conf hold the
  gateway settings.

Dashboard shell (frontend):
- Admin-only Observability page, route, and navigation entry, plus the
  observability service, its types, and the traces-pivot service.
- Each tab is guarded by the per-signal reachability check: an
  unreachable signal renders an explicit state rather than a broken
  panel. The signal panels themselves follow in later PRs of the stack.

Tests: backend specs for the gateway core, DTO validation, scope
resolver, rate limiter, and the MDC filters, plus frontend component and
service specs. sbt scalafmtCheckAll is clean.

Stacked on PR 3 (obs/pr3/deployment).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Ma77Ball
Ma77Ball force-pushed the obs/pr4/gateway-core branch from 907d120 to 681c046 Compare October 10, 2026 06:58
@Ma77Ball

Copy link
Copy Markdown
Contributor Author

Closing as part of scoping the observability work to instrumentation and export only. Per maintainer feedback on #5378, Texera will export OpenTelemetry data to external tools (Grafana, OpenSearch, Jaeger, etc.) instead of shipping its own query gateway and dashboard UI. The retained work is in #5375, #5376, #5377 (issues #5367, #5368, #5369).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci changes related to CI common dependencies Pull requests that update a dependency file docs Changes related to documentations engine frontend Changes related to the frontend GUI infra platform Non-amber Scala service paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Observability] Tenant-scoped query gateway and dashboard shell

2 participants