What's new in v0.9.2
v0.9.2 broadens framework coverage and closes two gaps in the SQL path. It adds Ruby and Active Record suggested-fix coverage, stops mis-classifying non-SQL datastore spans as SQL at ingestion, tokenizes MySQL backtick identifiers, and removes a raw-SQL leak from the self-contained HTML report. The pipeline stages, the daemon HTTP routes, the OTLP wire protocol, and the configuration format are unchanged. The surface that moves is the ingestion filter for non-SQL db.system values, the suggested_fix framework coverage, the SQL normalizer, and the HTML report payload. The minimum supported Rust version stays 1.96.0.
Detection: Ruby and Active Record suggestions
Framework-aware suggested_fix now covers Ruby. A finding on an Active Record stack, recognized from the OpenTelemetry::Instrumentation::ActiveRecord instrumentation scope or a .rb source path, carries Active Record remediation (includes, preload, eager_load), with a Ruby generic fallback across all ten anti-patterns. The new framework values on suggested_fix are ruby_active_record and ruby_generic. The active_record instrumentation scopes only record-loading queries, so an aggregate such as count carries the driver scope and resolves to the Ruby generic fix.
Ingestion: non-SQL datastore spans are dropped
A span whose db.system names a non-SQL datastore is dropped at ingestion instead of being fed to the SQL tokenizer. Previously a Redis GET user:123 or a MongoDB command was tokenized as SQL and could surface as a spurious n_plus_one_sql or redundant_sql finding. The recognized non-SQL systems are redis, memcached, mongodb, cassandra, dynamodb, couchbase, couchdb, elasticsearch, opensearch, neo4j, hbase, geode, and influxdb. The decision is gated on db.system alone and applied consistently on the OTLP, Jaeger, and Zipkin paths. A db.system that is absent or names a SQL engine is always treated as SQL, so no relational traffic is dropped. On the OTLP path the drop is counted under a new non_sql_datastore value of the reason label on perf_sentinel_otlp_spans_filtered_total, kept out of the daemon zero-retention warning so a cache-only fleet no longer raises a false instrumentation-gap alert.
Normalization: MySQL backtick identifiers
The SQL normalizer tokenizes MySQL backtick-quoted identifiers. A `column` is preserved verbatim and digits inside it are no longer extracted as literals, so a backtick-quoted N+1 groups under one template. The [ character is left untokenized on purpose, because it is overloaded by PostgreSQL array and subscript syntax. That keeps PostgreSQL array literals correctly redacted while common SQL Server [identifiers] still pass through.
Report: the HTML dashboard no longer embeds raw SQL
The self-contained HTML report no longer embeds the raw db.statement in its traces payload. The embedded-traces block carried the original statement verbatim, so a literal such as ARRAY['secret', 'pii'] reached the file even though the displayed template was masked. Only the masked template is embedded now. The rendered dashboard is unchanged, the raw value was a non-displayed fallback. The JSON, SARIF, terminal, and Prometheus exemplar outputs were already free of raw statements.
Operator-visible behavior change
A fleet that emitted non-SQL datastore spans carrying a db.statement will see those spans drop out of analysis. Any n_plus_one_sql or redundant_sql finding that came from Redis or MongoDB traffic disappears, and those spans no longer count toward the fanout and serialized-calls detectors. perf_sentinel_otlp_spans_filtered_total gains a non_sql_datastore value on its reason label, so a consumer that pins that label against a closed allowlist must accept it. The suggested_fix.framework field gains the ruby_active_record and ruby_generic values. No daemon HTTP route, OTLP wire shape, configuration key, or other Prometheus metric is added, removed, or changed.
Compatibility
The release is backward compatible. No CLI flag, configuration key, daemon HTTP route, or OTLP wire shape is removed or changes meaning. The additions are the Ruby suggested_fix coverage, the non_sql_datastore metric label value, and MySQL backtick tokenization. The two behavior changes, dropping non-SQL datastore spans and removing the raw statement from the HTML payload, are corrections to mis-classification and to literal redaction. The minimum supported Rust version stays 1.96.0.
Validation
The simulation-lab release-gate passed for v0.9.2 (lab commit 7ddd1c2, 2026-06-27), including a Rails 8 and Active Record service that emits the real OpenTelemetry::Instrumentation::ActiveRecord scope end to end. CI covers the build, the full test suite (cargo test --workspace), clippy at -D warnings, cargo fmt, and both the default and --no-default-features builds.
Verifying this release
# Binary integrity via SLSA Build L3 attestation
gh attestation verify perf-sentinel-linux-amd64 \
--owner robintra --repo perf-sentinel
# A periodic disclosure produced by this binary
perf-sentinel verify-hash --report perf-sentinel-report.json \
--expected-identity "https://github.com/robintra/perf-sentinel/.github/workflows/release.yml@refs/tags/v0.9.2" \
--expected-issuer "https://token.actions.githubusercontent.com"gh CLI 2.49 or newer required for gh attestation verify.
Full Changelog: v0.9.1...v0.9.2