github robintra/perf-sentinel v0.5.6

latest releases: chart-v0.2.53, v0.8.8, chart-v0.2.52...
one month ago

What's new in v0.5.6

Framework-aware suggestions now fire on Spring Boot + Spring Data JPA stacks. v0.5.5 shipped the code.* parent walker to recover attributes from ancestor spans, but suggested_fix still came back null on every JPA finding because the walker would stop on the user's Spring Data repository span where code.namespace is the user's class (e.g. com.example.OrderRepository), not a framework package. v0.5.6 closes the gap with two complementary signals: the OpenTelemetry instrumentation scope chain (io.opentelemetry.spring-data-3.0, io.opentelemetry.hibernate-6.0, etc.) captured at ingest, and the canonical Spring Data naming convention (*Repository, *Repo, *Dao) recognised on the user class itself. Either signal alone is enough to surface the JPA-specific suggested_fix; together they cover the realistic spectrum of how the OTel Java agent attributes user code to a framework.

After upgrading, a Spring Boot + JPA app instrumented with the default OTel Java agent produces findings with a populated framework: "java_jpa" and an actionable recommendation (JOIN FETCH / @EntityGraph for n_plus_one_sql, @Cacheable / first-level cache for redundant_sql). The same machinery applies to Quarkus reactive (io.opentelemetry.hibernate-reactive-1.0), Quarkus non-reactive (io.opentelemetry.quarkus-resteasy-reactive-3.0), Spring WebFlux (io.opentelemetry.spring-webflux-5.0, io.opentelemetry.r2dbc-1.0), so framework detection generalises beyond JPA.

Added

  • OpenTelemetry instrumentation scope chain captured per SpanEvent. New optional instrumentation_scopes: Vec<String> field on SpanEvent and Finding. Populated at OTLP ingest by walking the parent chain (bounded by 8 hops) and collecting each unique scopeSpans[].scope.name leaf to root. Empty on Jaeger / Zipkin / native JSON ingest where no scope info is carried. Skipped from JSON serialisation when empty, so the wire format stays backwards-compatible with v0.5.5 consumers.
  • Scope-based framework detection in detect_framework. New SCOPE_RULES table maps OTel scope short-names to internal frameworks: spring-data and hibernate to JPA, hibernate-reactive to Quarkus reactive, quarkus to Quarkus non-reactive, spring-webflux and r2dbc to WebFlux. Order matters (reactive variants ranked above the catch-all hibernate rule). The scope check runs BEFORE the namespace heuristics, so it dominates whenever the agent emits scope info. Boundary-aware match requires the io.opentelemetry. prefix and a segment boundary (- or end of string) after the needle, which excludes third-party tracers that happen to contain a framework name in their identifier (com.acme.quarkus-monitoring no longer matches the quarkus rule).
  • User-code naming convention hints in JAVA_RULES. Three new LastSegmentEndsWith patterns (Repository, Repo, Dao) in the JPA rule. Catches Spring Data repositories whose code.namespace is com.example.OrderRepository rather than a Spring framework package. Existing framework-package substrings (org.springframework.data.jpa, org.hibernate, etc.) keep priority and still match first.
  • Namespace-only fallback when code.filepath is absent. OTel agents often emit code.namespace on a parent span without code.filepath. v0.5.5 returned None from the detector in that case, so no fix attached. v0.5.6 falls back to iterating every language's rules in order (Java, C#, Rust) when the filepath is missing, returning the first match. No language-generic fallback fires here because we cannot pick one without a filepath signal.
  • (RedundantSql, JavaJpa) mapping in the FIXES table. Was missing in v0.5.5 even though detection could identify JPA: the lab finding type is redundant_sql, so the lookup returned None. The new entry recommends @Cacheable on the repository or service method, or sharing the EntityManager within the request via @Transactional so Hibernate's first-level cache deduplicates the read. Reference URL points at the Spring Cache abstraction docs.

Fixed

  • Cross-trace slow findings now carry code_location and instrumentation_scopes from the worst trace. detect_slow_cross_trace previously emitted these fields as None / empty even when a representative span existed in the entries. The internal entry tuple now carries &SpanEvent so the framework-detection path runs on cross-trace findings too, matching the per-trace detectors' contract. JPA, Hibernate and friends are now recognised on cross-trace slow findings.

Security

  • Sanitize instrumentation_scopes at the ingest boundary. New sanitize_string_vec helper drops elements with ASCII control characters, truncates each scope to 256 bytes, and caps the Vec at 8 elements. Closes the regression introduced when the field landed without going through the canonical sanitize_span_event path: an attacker-supplied OTLP payload could otherwise stuff a 1 MB control-char-laden scope name into every event, amplified again on each Finding.instrumentation_scopes.clone(). The cap matches the OTLP parent-walk bound and also fires on the JSON ingest path which has no structural depth bound.

Changed

  • Single-pass attribute classifier in convert_span. Replaces ~14 separate linear scans over the OTLP attribute list with one pass that fills a ClassifiedAttrs<'_> struct via match on the key. ~13x fewer key comparisons per span at typical 30-attribute HTTP spans, with no allocation regression.
  • Helm chart 0.2.9 bumps appVersion and the default daemon image tag to 0.5.6. Drop the workaround compression: none on the OTel Collector now that gzip is supported natively (already true since v0.5.5, this release just consolidates the framework detection on top).

Install

Prebuilt binaries (Linux amd64 / arm64, macOS arm64, Windows amd64):

curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.6/perf-sentinel-linux-amd64
chmod +x perf-sentinel-linux-amd64
sudo mv perf-sentinel-linux-amd64 /usr/local/bin/perf-sentinel

Linux binaries are statically linked against musl and run on any distribution (Alpine, Debian, RHEL, Ubuntu any version) regardless of glibc version, and inside FROM scratch images.

From crates.io:

cargo install perf-sentinel

Docker:

docker run --rm -p 4317:4317 -p 4318:4318 \
  ghcr.io/robintra/perf-sentinel:0.5.6 watch --listen-address 0.0.0.0

Also available on Docker Hub: robintrassard/perf-sentinel:0.5.6.

Helm (chart 0.2.9 ships 0.5.6 as its appVersion default):

helm install perf-sentinel oci://ghcr.io/robintra/charts/perf-sentinel \
  --version 0.2.9 \
  --namespace observability --create-namespace

Verify the binary against SHA256SUMS.txt:

curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.6/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missing

Full diff: v0.5.5...v0.5.6

Don't miss a new perf-sentinel release

NewReleases is sending notifications on new releases.