chronos-ruby 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +22 -10
- data/contracts/apm-batch-v1.schema.json +51 -1
- data/docs/adr/ADR-015-bounded-apm-aggregation.md +3 -3
- data/docs/adr/ADR-019-bounded-query-diagnostics.md +44 -0
- data/docs/architecture.md +5 -2
- data/docs/compatibility.md +4 -4
- data/docs/configuration.md +21 -0
- data/docs/data-collected.md +9 -3
- data/docs/examples/plain-ruby.md +6 -0
- data/docs/modules/apm-aggregation.md +18 -5
- data/docs/modules/sidekiq-legacy.md +1 -1
- data/docs/modules/sql-monitoring.md +60 -6
- data/docs/modules/telemetry-events.md +1 -1
- data/docs/performance.md +19 -3
- data/docs/privacy-lgpd.md +6 -3
- data/docs/protocol-v1.md +1 -1
- data/docs/release-1.1-readiness.md +51 -0
- data/docs/security-review.md +7 -3
- data/docs/troubleshooting.md +6 -0
- data/lib/chronos/agent.rb +12 -2
- data/lib/chronos/application/apm_aggregator.rb +179 -29
- data/lib/chronos/configuration/apm_validation.rb +51 -1
- data/lib/chronos/configuration.rb +17 -1
- data/lib/chronos/core/metric_aggregate.rb +69 -7
- data/lib/chronos/core/sql_query_analyzer.rb +309 -0
- data/lib/chronos/ports/query_inspector.rb +23 -0
- data/lib/chronos/rails/active_record_query_inspector.rb +235 -0
- data/lib/chronos/rails/notifications_subscriber.rb +163 -2
- data/lib/chronos/rails.rb +1 -0
- data/lib/chronos/version.rb +1 -1
- data/lib/chronos.rb +2 -0
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0127def72275a2d4c08314040c04e34a9606fed13982d84b06c465dee7be4cf6
|
|
4
|
+
data.tar.gz: 99f272314517e6a525223ce86964b70fcdfce717eb0e434b9c77e275031913b7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 93b37af1a5b51284a6c6de1c19e12de18ac23b04bfa4a04f33b7fce5cb611ec6f940d4ec2508c8a2b8d72f91e9a2a9a812a3b48d72f60dfefd642fb7cf3fe138
|
|
7
|
+
data.tar.gz: 4c653acb6d7aa90d534a7d496568fca64fb9ba9bce9039c6ca68fad71c1b5aef3f4e5de541a3f3d0f6064f08653359bcc2afc30e3a2d82c910942f0c1e103d4a
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes are documented here. The project follows Semantic Versioning
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.1.0] - 2026-08-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- bounded normalized SELECT analysis with access columns, index candidates, existing-index comparison, optional table statistics, and allowlisted non-executing query plans;
|
|
12
|
+
- structured query diagnostics and severity counts for errors, warnings, information, and suggestions, including actionable N+1 and index guidance;
|
|
13
|
+
- approximate p50/p95/p99 metrics, complete outer-transaction timing, expanded adapter error families, and trace/fingerprint loss counters;
|
|
14
|
+
- explicit low-risk defaults and bounded opt-in configuration for database index/statistics/plan inspection.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- aggregate drains preserve active trace trackers until request completion or idle TTL instead of discarding incomplete correlation;
|
|
19
|
+
- possible N+1 now requires a non-cached SELECT, and query analysis sent to consumers prefers inspected evidence over an earlier static-only observation.
|
|
20
|
+
|
|
21
|
+
### Security
|
|
22
|
+
|
|
23
|
+
- query inspection never uses `EXPLAIN ANALYZE`, never executes DDL, omits raw SQL/binds/predicates/messages, and retains only bounded schema and planner fields.
|
|
24
|
+
|
|
7
25
|
## [1.0.0] - 2026-07-29
|
|
8
26
|
|
|
9
27
|
### Added
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Chronos Ruby
|
|
2
2
|
|
|
3
|
-
Chronos Ruby 1.
|
|
3
|
+
Chronos Ruby 1.1.0 é o agente independente de framework para enviar exceções e telemetria limitada de aplicações Ruby ao Chronos. Esta é a linha estável legado, compatível com o protocolo v1 e voltada a Ruby 2.2.10–2.6.
|
|
4
4
|
|
|
5
5
|
## O que a gem coleta
|
|
6
6
|
|
|
7
|
-
A versão 1.
|
|
7
|
+
A versão 1.1 pode coletar:
|
|
8
8
|
|
|
9
9
|
- classe, mensagem, backtrace estruturado e causas encadeadas da exceção;
|
|
10
10
|
- timestamp, severidade, tags e fingerprint opcional;
|
|
@@ -20,11 +20,11 @@ Veja a tabela completa em [Dados coletados](docs/data-collected.md).
|
|
|
20
20
|
|
|
21
21
|
## O que não é coletado por padrão
|
|
22
22
|
|
|
23
|
-
A gem não varre variáveis de ambiente, sistema de arquivos ou lockfiles e não
|
|
23
|
+
A gem não varre variáveis de ambiente, sistema de arquivos ou lockfiles e não coleta bodies HTTP, cookies, headers de autorização, conteúdo de e-mail, SQL bruto, binds, valores de cache ou código-fonte. A inspeção de plano, desativada por padrão, usa o SQL original somente na conexão local para `EXPLAIN` sem `ANALYZE` e o descarta. O inventário de dependências contém somente nomes e versões já carregados, uma vez por agente. A aplicação continua responsável por minimização e base legal dos dados enviados.
|
|
24
24
|
|
|
25
25
|
## Versões Ruby e Rails suportadas
|
|
26
26
|
|
|
27
|
-
A versão 1.
|
|
27
|
+
A versão 1.1.0 suporta Ruby puro e Rack em Ruby 2.2.10, 2.3.8, 2.4.10, 2.5.9 e 2.6.10. As combinações Rails validadas são Rails 4.2 com Ruby 2.2.10/2.3.8 e Rails 5.2 com Ruby 2.5.9/2.6.10. Sidekiq 4.2.10 com Ruby 2.2.10 e Sidekiq 5.2.10 com Ruby 2.5.9 também possuem gates dedicados. Ruby 2.7/Rails 6 não é declarado nesta release porque ainda não possui aplicação e matriz completas.
|
|
28
28
|
|
|
29
29
|
Rubies e frameworks antigos estão fora do suporte de segurança de seus mantenedores. A Chronos oferece compatibilidade técnica, não manutenção de segurança do runtime. Veja [Compatibilidade](docs/compatibility.md).
|
|
30
30
|
|
|
@@ -33,7 +33,7 @@ Rubies e frameworks antigos estão fora do suporte de segurança de seus mantene
|
|
|
33
33
|
Obrigatório: adicione a versão estável ao `Gemfile`.
|
|
34
34
|
|
|
35
35
|
```ruby
|
|
36
|
-
gem "chronos-ruby", "~> 1.
|
|
36
|
+
gem "chronos-ruby", "~> 1.1.0"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Em runtimes antigos, use Bundler compatível:
|
|
@@ -46,7 +46,7 @@ bundle _1.17.3_ install
|
|
|
46
46
|
Sem Bundler:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
gem install chronos-ruby -v 1.
|
|
49
|
+
gem install chronos-ruby -v 1.1.0
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
## Instalação em Rails
|
|
@@ -54,7 +54,7 @@ gem install chronos-ruby -v 1.0.0
|
|
|
54
54
|
Obrigatório: carregue a integração Rails explicitamente para manter Rails/ActiveSupport fora de aplicações Ruby puras.
|
|
55
55
|
|
|
56
56
|
```ruby
|
|
57
|
-
gem "chronos-ruby", "~> 1.
|
|
57
|
+
gem "chronos-ruby", "~> 1.1.0", :require => "chronos/rails"
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Gere o initializer:
|
|
@@ -174,7 +174,7 @@ A regra recebe um notice normalizado e imutável, e somente `true` descarta. Fal
|
|
|
174
174
|
|
|
175
175
|
## Monitoramento de performance
|
|
176
176
|
|
|
177
|
-
A Versão 0.7 introduziu agregação local de requests, queries e jobs em `metric_batch`; a Versão 0.8 adicionou HTTP externo. Grupos possuem contagem, erro, duração, histograma, status e breakdown limitados.
|
|
177
|
+
A Versão 0.7 introduziu agregação local de requests, queries e jobs em `metric_batch`; a Versão 0.8 adicionou HTTP externo. Grupos possuem contagem, erro, duração, histograma, percentis aproximados, severidades, diagnósticos, status e breakdown limitados.
|
|
178
178
|
|
|
179
179
|
```ruby
|
|
180
180
|
Chronos.configure do |config|
|
|
@@ -185,10 +185,22 @@ Chronos.configure do |config|
|
|
|
185
185
|
config.apm_max_queries_per_request = 100
|
|
186
186
|
config.apm_slow_query_threshold_ms = 500.0
|
|
187
187
|
config.apm_n_plus_one_threshold = 5
|
|
188
|
+
config.apm_trace_ttl_seconds = 60.0
|
|
189
|
+
config.apm_query_analysis_enabled = true
|
|
190
|
+
config.apm_query_analysis_max_queries = 100
|
|
191
|
+
|
|
192
|
+
# Opt-in: cada fingerprint elegível pode consultar catálogo/estatística/plano.
|
|
193
|
+
config.apm_query_inspection_enabled = false
|
|
194
|
+
config.apm_query_statistics_enabled = false
|
|
195
|
+
config.apm_query_plan_enabled = false
|
|
196
|
+
config.apm_query_inspection_min_duration_ms = 500.0
|
|
197
|
+
config.apm_query_inspection_max_queries = 20
|
|
198
|
+
config.apm_transaction_tracking_enabled = true
|
|
199
|
+
config.apm_transaction_max_connections = 100
|
|
188
200
|
end
|
|
189
201
|
```
|
|
190
202
|
|
|
191
|
-
SQL bruto e binds não são lidos
|
|
203
|
+
Por padrão, SQL bruto e binds não são lidos pelo pipeline de análise. A inspeção opt-in usa o SQL original apenas localmente para solicitar `EXPLAIN` sem `ANALYZE`; nunca o inclui no evento. A análise estática produz candidatos, não ordens de criação de índice. Erros usam severidade `error`; lentidão e risco usam `warning`; padrões observados usam `info`; correções propostas usam `suggestion`. Veja [APM](docs/modules/apm-aggregation.md), [Requests](docs/modules/request-monitoring.md) e [SQL](docs/modules/sql-monitoring.md).
|
|
192
204
|
|
|
193
205
|
## Sidekiq e Active Job
|
|
194
206
|
|
|
@@ -196,7 +208,7 @@ A versão `0.6.0.pre.1` introduziu middleware Sidekiq 4/5; a API estável manté
|
|
|
196
208
|
|
|
197
209
|
```ruby
|
|
198
210
|
gem "sidekiq", "~> 5.0"
|
|
199
|
-
gem "chronos-ruby", "~> 1.
|
|
211
|
+
gem "chronos-ruby", "~> 1.1.0", :require => "chronos/sidekiq"
|
|
200
212
|
```
|
|
201
213
|
|
|
202
214
|
O envelope de contexto não altera argumentos públicos e contém somente IDs limitados de trace/request. Active Job usa um campo serializado com namespace (`chronos_context`) e hooks públicos. Erros aninhados são deduplicados e reerguidos. Veja [Sidekiq legado](docs/modules/sidekiq-legacy.md), [Active Job](docs/modules/active-job.md) e [Jobs](docs/modules/job-monitoring.md).
|
|
@@ -24,10 +24,60 @@
|
|
|
24
24
|
"type": "object",
|
|
25
25
|
"required": ["total", "min", "max", "average"]
|
|
26
26
|
},
|
|
27
|
+
"percentiles_ms": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"additionalProperties": false,
|
|
30
|
+
"required": ["p50", "p95", "p99"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"p50": {"type": "number", "minimum": 0},
|
|
33
|
+
"p95": {"type": "number", "minimum": 0},
|
|
34
|
+
"p99": {"type": "number", "minimum": 0}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
27
37
|
"histogram": {"type": "array", "maxItems": 20},
|
|
28
38
|
"breakdown_ms": {"type": "object"},
|
|
29
39
|
"signals": {"type": "object"},
|
|
30
|
-
"status_codes": {"type": "object"}
|
|
40
|
+
"status_codes": {"type": "object"},
|
|
41
|
+
"severity_counts": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"additionalProperties": false,
|
|
44
|
+
"properties": {
|
|
45
|
+
"error": {"type": "integer", "minimum": 0},
|
|
46
|
+
"warning": {"type": "integer", "minimum": 0},
|
|
47
|
+
"info": {"type": "integer", "minimum": 0},
|
|
48
|
+
"suggestion": {"type": "integer", "minimum": 0}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"diagnostics": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"maxItems": 20,
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"required": ["code", "severity", "category", "message", "evidence", "count"],
|
|
58
|
+
"properties": {
|
|
59
|
+
"code": {"type": "string", "maxLength": 128},
|
|
60
|
+
"severity": {"enum": ["error", "warning", "info", "suggestion"]},
|
|
61
|
+
"category": {"type": "string", "maxLength": 64},
|
|
62
|
+
"message": {"type": "string", "maxLength": 512},
|
|
63
|
+
"recommendation": {"type": "string", "maxLength": 512},
|
|
64
|
+
"evidence": {"type": "object"},
|
|
65
|
+
"count": {"type": "integer", "minimum": 1}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"query_analysis": {"type": "object"},
|
|
70
|
+
"tracking": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"required": ["active_traces", "dropped_trace_trackers", "expired_trace_trackers", "dropped_query_fingerprints"],
|
|
74
|
+
"properties": {
|
|
75
|
+
"active_traces": {"type": "integer", "minimum": 0},
|
|
76
|
+
"dropped_trace_trackers": {"type": "integer", "minimum": 0},
|
|
77
|
+
"expired_trace_trackers": {"type": "integer", "minimum": 0},
|
|
78
|
+
"dropped_query_fingerprints": {"type": "integer", "minimum": 0}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
31
81
|
}
|
|
32
82
|
}
|
|
33
83
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
|
-
Accepted for version 0.7.
|
|
5
|
+
Accepted for version 0.7; trace-drain behavior amended by ADR-019.
|
|
6
6
|
|
|
7
7
|
## Context
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ Sending every request, SQL query, and job as an independent event increases netw
|
|
|
12
12
|
|
|
13
13
|
Aggregate request, query, and job observations by bounded low-cardinality dimensions. Store only counters, error counters, duration sum/min/max, fixed histogram buckets, component totals, status counters, and heuristic signal counters. Calculate averages locally and percentiles in the SaaS.
|
|
14
14
|
|
|
15
|
-
Use a fixed group limit, fixed query-fingerprint limit per trace, fixed batch size, and no new timer thread. Drain on observation threshold, explicit flush, and close.
|
|
15
|
+
Use a fixed group limit, fixed query-fingerprint limit per trace, fixed batch size, and no new timer thread. Drain on observation threshold, explicit flush, and close. ADR-019 later preserves incomplete trace trackers across drains under capacity and idle-TTL limits. Normalize SQL comments and literal values before fingerprinting and never read binds. Use a `metric_batch` event through the existing sanitization, queue, retry, circuit breaker, and backlog pipeline.
|
|
16
16
|
|
|
17
17
|
## Alternatives
|
|
18
18
|
|
|
@@ -24,4 +24,4 @@ Delivery volume is reduced, memory remains bounded, request breakdown is availab
|
|
|
24
24
|
|
|
25
25
|
## Negative consequences
|
|
26
26
|
|
|
27
|
-
Process crashes can lose undrained aggregates,
|
|
27
|
+
Process crashes can lose undrained aggregates, idle/over-capacity trackers can be discarded and are counted, local signals are heuristic, and a defensive SQL normalizer cannot understand every database dialect.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ADR-019 — Bounded query diagnostics and opt-in database inspection
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Fingerprint repetition and duration identify useful SQL symptoms but do not tell the consumer which access columns were observed, whether an existing index covers them, or what the database planner estimated. Running deep analysis inside a legacy application can add latency, expose data, increase cardinality, and violate the fixed-memory requirement.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Analyze normalized SELECT text by default with a dependency-free bounded heuristic. Emit structured diagnostics with stable code, severity (`error`, `warning`, `info`, `suggestion`), category, bounded evidence, count, and optional recommendation.
|
|
14
|
+
|
|
15
|
+
Define `Ports::QueryInspector` and keep its ActiveRecord implementation in `Chronos::Rails`. Index, statistics, and plan inspection are independent explicit opt-ins. Inspect each eligible fingerprint at most once per subscriber, cap inspected fingerprints, and require a duration threshold. Plans use `EXPLAIN` only, never `EXPLAIN ANALYZE`; DDL and automatic index creation are prohibited. Retain only allowlisted schema/planner fields and exception class names. Never retain raw SQL, binds, plan predicates, arbitrary rows, or exception messages.
|
|
16
|
+
|
|
17
|
+
Preserve trace trackers across metric drains with capacity and idle TTL. Report loss counters. Measure outer transaction time with bounded per-connection notification state. Derive approximate p50/p95/p99 from fixed histograms without retaining duration samples.
|
|
18
|
+
|
|
19
|
+
Keep new metric fields optional in the v1 JSON Schema for backward compatibility, although the new agent emits them. Consumer models use diagnostic code/severity as stable fields and treat message text as a fallback presentation value.
|
|
20
|
+
|
|
21
|
+
## Alternatives
|
|
22
|
+
|
|
23
|
+
- Server-only static analysis was rejected because existing-index comparison requires local schema evidence and immediate signals remain useful.
|
|
24
|
+
- Automatic `EXPLAIN ANALYZE` was rejected because it executes the query.
|
|
25
|
+
- Automatic `CREATE INDEX` was rejected because it can block writes, consume storage, and regress mutation workloads.
|
|
26
|
+
- Shipping raw SQL/plans was rejected for privacy and cardinality.
|
|
27
|
+
- A full SQL-parser dependency was rejected for legacy compatibility, installation cost, and dialect complexity.
|
|
28
|
+
- Unbounded trace/inspection caches were rejected for memory safety.
|
|
29
|
+
|
|
30
|
+
## Positive consequences
|
|
31
|
+
|
|
32
|
+
- Consumers receive actionable, typed evidence with clear confidence limits.
|
|
33
|
+
- Default analysis adds no database round trip.
|
|
34
|
+
- Opt-in inspection is read-only, sampled by fingerprint/duration, and contains adapter failures.
|
|
35
|
+
- Errors, warnings, information, and suggestions can map directly to observer models.
|
|
36
|
+
- Trace correlation no longer disappears at every aggregate flush.
|
|
37
|
+
|
|
38
|
+
## Negative consequences
|
|
39
|
+
|
|
40
|
+
- Static parsing can miss complex SQL, aliases, expressions, partial indexes, and dialect features.
|
|
41
|
+
- Even non-executing `EXPLAIN` and catalog reads consume a connection/planner budget and have no portable legacy timeout.
|
|
42
|
+
- Index recommendations do not know true selectivity, write cost, storage, or production distribution.
|
|
43
|
+
- Approximate percentiles use histogram upper bounds and may be coarse.
|
|
44
|
+
- Transaction elapsed time is measured between notification callbacks, not database wire boundaries.
|
data/docs/architecture.md
CHANGED
|
@@ -20,7 +20,10 @@ flowchart TB
|
|
|
20
20
|
Sidekiq --> Envelope[Versioned job-envelope context]
|
|
21
21
|
Application --> Telemetry[Core / TelemetryEvent]
|
|
22
22
|
Telemetry --> APM[Application / ApmAggregator]
|
|
23
|
-
APM --> Metrics[Core / MetricAggregate and
|
|
23
|
+
APM --> Metrics[Core / MetricAggregate, SqlNormalizer, and SqlQueryAnalyzer]
|
|
24
|
+
Rails --> Inspector[ActiveRecordQueryInspector opt-in]
|
|
25
|
+
Inspector --> QueryPort[Ports / QueryInspector]
|
|
26
|
+
Inspector --> Metrics
|
|
24
27
|
NetHTTP[Explicit Net::HTTP instance wrapper] --> Facade
|
|
25
28
|
Rails --> Cache[Core / CacheNormalizer]
|
|
26
29
|
Application --> Dependencies[Application / DependencyReporter]
|
|
@@ -53,7 +56,7 @@ Rails timings become immutable `TelemetryEvent` values. `CaptureTelemetry` appli
|
|
|
53
56
|
|
|
54
57
|
Sidekiq client middleware writes a versioned, allowlisted trace/request context beside the job's public arguments. Server middleware restores a job scope, limits arguments and tags, emits a job observation, and routes a failure through the existing notice pipeline before re-raising it. Sidekiq retains queue, retry, thread, and connection lifecycle ownership.
|
|
55
58
|
|
|
56
|
-
Version 0.7 routes request, query, and job observations through `ApmAggregator`. `SqlNormalizer` removes values before grouping,
|
|
59
|
+
Version 0.7 routes request, query, and job observations through `ApmAggregator`. `SqlNormalizer` removes values before grouping, `SqlQueryAnalyzer` derives bounded access evidence, and `MetricAggregate` owns fixed numerical statistics, approximate percentiles, severities, diagnostics, and one representative analysis. Active trace trackers survive group drains, expire by TTL, and expose loss counters. `ActiveRecordQueryInspector` is an optional Rails adapter behind `Ports::QueryInspector`; it reads public index metadata and allowlisted PostgreSQL/MySQL statistics/plans without `ANALYZE` or DDL. Threshold or lifecycle drains create `metric_batch` telemetry that passes through the existing sanitizer and delivery pipeline. No APM-specific thread is created.
|
|
57
60
|
|
|
58
61
|
Version 0.8 prepends a wrapper only to each explicitly selected `Net::HTTP` object. The wrapper records bounded outcome metadata and feeds the existing APM aggregator; it does not modify `Net::HTTP` globally. `CacheNormalizer` turns public Rails cache notifications into bounded metadata before delivery. `DependencyReporter` reads already loaded runtime metadata once per agent under a mutex and queues it as a separate event.
|
|
59
62
|
|
data/docs/compatibility.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Compatibility
|
|
2
2
|
|
|
3
|
-
Chronos Ruby 1.
|
|
3
|
+
Chronos Ruby 1.1 is the current stable legacy line. Technical compatibility does not make an end-of-life Ruby, Rails, Rack, or Sidekiq release secure. The planned Ruby 2.7/Rails 6 transitional matrix remains deferred until it has dedicated CI and a real application gate.
|
|
4
4
|
|
|
5
5
|
## Core and Rack
|
|
6
6
|
|
|
@@ -33,11 +33,11 @@ Chronos Ruby 1.0 is the stable legacy line. Technical compatibility does not mak
|
|
|
33
33
|
|
|
34
34
|
Active Job uses the public `serialize`, `deserialize`, and `perform_now` extension points with a bounded namespaced field. Support follows the validated Rails pairs above. Adapters that bypass these hooks require their own evidence.
|
|
35
35
|
|
|
36
|
-
The release workflow repeats every supported pair before publishing.
|
|
36
|
+
The release workflow repeats every supported pair before publishing. Historical 1.0 evidence remains in [Version 1.0 readiness](release-1.0-readiness.md); the 1.1 candidate is recorded in [Version 1.1 readiness](release-1.1-readiness.md).
|
|
37
37
|
|
|
38
38
|
Status meanings:
|
|
39
39
|
|
|
40
40
|
- Supported: every mandatory compatibility gate for the exact pair passes.
|
|
41
|
-
- Best effort: intended to work but missing a complete gate; no 1.
|
|
41
|
+
- Best effort: intended to work but missing a complete gate; no 1.1 pair is advertised this way.
|
|
42
42
|
- Deprecated: still tested while removal is planned.
|
|
43
|
-
- Unsupported: outside the tested 1.
|
|
43
|
+
- Unsupported: outside the tested 1.1 contract.
|
data/docs/configuration.md
CHANGED
|
@@ -63,6 +63,16 @@
|
|
|
63
63
|
| `apm_long_transaction_threshold_ms` | Optional | `1000.0` | Transaction-labelled SQL duration that produces a signal |
|
|
64
64
|
| `apm_n_plus_one_threshold` | Optional | `5` | Repeated fingerprint count producing one possible-N+1 signal; minimum 2 |
|
|
65
65
|
| `apm_histogram_buckets` | Optional | Fixed millisecond boundaries | Increasing positive duration boundaries; at most 19 plus `+Inf` |
|
|
66
|
+
| `apm_trace_ttl_seconds` | Optional | `60.0` | Maximum idle time retained for an incomplete trace or transaction tracker |
|
|
67
|
+
| `apm_query_analysis_enabled` | Optional | `true` | Analyzes bounded normalized SELECT shapes and emits informational/index-candidate diagnostics |
|
|
68
|
+
| `apm_query_analysis_max_queries` | Optional | `100` | Maximum normalized fingerprints analyzed and cached per subscriber; range 1–500 |
|
|
69
|
+
| `apm_query_inspection_enabled` | Optional | `false` | Opts in to read-only comparison with ActiveRecord index metadata |
|
|
70
|
+
| `apm_query_statistics_enabled` | Optional | `false` | Adds adapter-specific estimated table-row statistics; requires query inspection |
|
|
71
|
+
| `apm_query_plan_enabled` | Optional | `false` | Adds allowlisted `EXPLAIN` evidence for eligible SELECTs; never uses `ANALYZE` |
|
|
72
|
+
| `apm_query_inspection_min_duration_ms` | Optional | `500.0` | Minimum observed query duration before an opt-in database inspection |
|
|
73
|
+
| `apm_query_inspection_max_queries` | Optional | `20` | Maximum unique fingerprints inspected per subscriber; range 1–100 |
|
|
74
|
+
| `apm_transaction_tracking_enabled` | Optional | `true` | Measures bounded outer transaction elapsed time by notification connection |
|
|
75
|
+
| `apm_transaction_max_connections` | Optional | `100` | Maximum concurrently tracked transaction connections; range 1–500 |
|
|
66
76
|
| `external_http_enabled` | Optional | `false` | Allows explicit per-instance outbound `Net::HTTP` instrumentation |
|
|
67
77
|
| `external_http_trace_headers` | Optional | `true` | Propagates Chronos trace/request headers on instrumented requests |
|
|
68
78
|
| `cache_key_mode` | Optional | `:none` | `:none` omits keys; `:sha256` emits a project-scoped key hash |
|
|
@@ -102,6 +112,17 @@ Chronos.configure do |config|
|
|
|
102
112
|
config.apm_slow_query_threshold_ms = 500.0
|
|
103
113
|
config.apm_long_transaction_threshold_ms = 1000.0
|
|
104
114
|
config.apm_n_plus_one_threshold = 5
|
|
115
|
+
config.apm_trace_ttl_seconds = 60.0
|
|
116
|
+
config.apm_query_analysis_enabled = true
|
|
117
|
+
config.apm_query_analysis_max_queries = 100
|
|
118
|
+
# Database access is explicit because each inspected fingerprint adds read-only work.
|
|
119
|
+
config.apm_query_inspection_enabled = false
|
|
120
|
+
config.apm_query_statistics_enabled = false
|
|
121
|
+
config.apm_query_plan_enabled = false
|
|
122
|
+
config.apm_query_inspection_min_duration_ms = 500.0
|
|
123
|
+
config.apm_query_inspection_max_queries = 20
|
|
124
|
+
config.apm_transaction_tracking_enabled = true
|
|
125
|
+
config.apm_transaction_max_connections = 100
|
|
105
126
|
config.external_http_enabled = false
|
|
106
127
|
config.external_http_trace_headers = true
|
|
107
128
|
config.cache_key_mode = :none
|
data/docs/data-collected.md
CHANGED
|
@@ -33,13 +33,19 @@ Version 0.9 emits exceptions, cache telemetry, dependency/deploy events, and agg
|
|
|
33
33
|
| Sidekiq arguments | Collected, sanitized, and bounded | First 20 job arguments; collections/depth/strings limited |
|
|
34
34
|
| Sidekiq tags | Collected and bounded | Job payload or public worker options |
|
|
35
35
|
| Sidekiq trace/request IDs | Propagated when present; trace generated otherwise | Chronos job-envelope metadata |
|
|
36
|
-
| APM counts, error counts/rates, duration total/min/max/average | Aggregated by default | Request, query, and job observations |
|
|
37
|
-
| Fixed duration histogram
|
|
36
|
+
| APM counts, error counts/rates, duration total/min/max/average and approximate p50/p95/p99 | Aggregated by default | Request, query, and job observations plus fixed histogram |
|
|
37
|
+
| Fixed duration histogram, status counts, severity counts and bounded diagnostics | Aggregated by default | Local bounded counters and classifiers |
|
|
38
38
|
| Component breakdown | database/view/external_http/cache/queue/application when observable | Trace-local bounded totals |
|
|
39
39
|
| Normalized SQL and SHA-256 fingerprint | Collected without comments, literals, or binds | `sql.active_record` payload |
|
|
40
40
|
| SQL adapter, operation, inferred table, AR name, cache flag, role/shard | Collected when exposed | Public notification payload and connection feature detection |
|
|
41
41
|
| Slow SQL source frame | Collected only for threshold-selected slow queries | Bounded application call frame |
|
|
42
42
|
| APM diagnostic signals | Heuristic counters | Local threshold and repetition detection |
|
|
43
|
+
| SELECT tables and equality/range/join/order columns; index candidates | Collected by default from normalized SQL; bounded | Local static query analyzer |
|
|
44
|
+
| Existing index names/columns/uniqueness | Disabled by default; read-only opt-in | Public ActiveRecord schema metadata |
|
|
45
|
+
| Estimated table rows | Disabled by default; read-only opt-in | PostgreSQL `pg_class` or MySQL `information_schema` |
|
|
46
|
+
| Allowlisted plan node type/table/index/estimated rows/cost | Disabled by default; `EXPLAIN` without `ANALYZE` | Local database planner |
|
|
47
|
+
| Complete outer transaction approximate duration | Collected by default with bounded state | BEGIN/COMMIT/ROLLBACK notifications keyed by local connection identity |
|
|
48
|
+
| Trace tracking loss/expiration counters | Aggregated by default | Local bounded tracker lifecycle |
|
|
43
49
|
| External HTTP host, method, status, duration, timeout, connection-error flag, error class | Disabled by default; per-instance opt-in | Instrumented `Net::HTTP` object |
|
|
44
50
|
| Chronos trace/request headers | Propagated when available | Current execution context |
|
|
45
51
|
| Cache operation, backend, namespace, hit/miss, duration | Collected; key/value omitted | ActiveSupport cache notifications |
|
|
@@ -50,7 +56,7 @@ Version 0.9 emits exceptions, cache telemetry, dependency/deploy events, and agg
|
|
|
50
56
|
| Deploy environment, revision, version, repository, actor, deploy ID, service, region, instance | Explicit synchronous deploy API | Application/deployment integration arguments |
|
|
51
57
|
| Integration verification ID, fixed test marker, synthetic exception class/message, and receipt correlation | Only when `Chronos.verify_integration` or its Rake task is invoked | Locally generated bounded values |
|
|
52
58
|
|
|
53
|
-
The gem never
|
|
59
|
+
The gem never transmits request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, Git state, source code, raw SQL, SQL bind values, database rows, plan predicates, raw cache keys/values, mail bodies/recipients, Active Job arguments, gem paths, or lockfile contents. Opt-in plan inspection temporarily supplies the already observed local SELECT to `EXPLAIN` without `ANALYZE` and discards it. Active Job IDs, Sidekiq JIDs/arguments, loaded gem names/versions, and deploy fields are documented integration data.
|
|
54
60
|
|
|
55
61
|
APM dimensions never include user ID, job ID, raw URL, exception message, bind value, or cache key. Normalized routes replace common numeric/UUID segments. Normalized SQL can retain schema, table, and column identifiers; review those identifiers as part of the privacy audit.
|
|
56
62
|
|
data/docs/examples/plain-ruby.md
CHANGED
|
@@ -29,6 +29,12 @@ For network-free per-instance outbound HTTP instrumentation, run:
|
|
|
29
29
|
bundle _1.17.3_ exec ruby examples/plain-ruby/external_http.rb
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
For a network-free normalized-query/index diagnostic example, run:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bundle _1.17.3_ exec ruby examples/plain-ruby/query_analysis.rb
|
|
36
|
+
```
|
|
37
|
+
|
|
32
38
|
For a network-free deploy event with complete release correlation, run:
|
|
33
39
|
|
|
34
40
|
```bash
|
|
@@ -4,13 +4,13 @@ Version `0.8.0.pre.1` aggregates request, SQL, job, and explicitly instrumented
|
|
|
4
4
|
|
|
5
5
|
## Request metrics
|
|
6
6
|
|
|
7
|
-
Request groups use normalized route and HTTP method. Status codes are counted inside the group so error rate can be calculated without making status part of an unbounded key. Each metric contains count, error count/rate, total, minimum, maximum, average, fixed histogram buckets, and component breakdown.
|
|
7
|
+
Request groups use normalized route and HTTP method. Status codes are counted inside the group so error rate can be calculated without making status part of an unbounded key. Each metric contains count, error count/rate, total, minimum, maximum, average, fixed histogram buckets, approximate p50/p95/p99 upper bounds, severity counts, bounded diagnostics, and component breakdown. The SaaS may calculate more precise percentiles from merged histograms; the agent never retains raw duration arrays.
|
|
8
8
|
|
|
9
9
|
Rack records request metrics for non-Rails applications. Rails controller notifications and Rack share `record_event_once`, so the same request is counted only once. Controller notification data wins when available because its route is more precise.
|
|
10
10
|
|
|
11
11
|
## SQL metrics and signals
|
|
12
12
|
|
|
13
|
-
`Chronos::Core::SqlNormalizer` removes block/line comments, quoted literal values, numeric values, booleans, nulls, and repeated `IN` values before producing a SHA-256 fingerprint. Binds are never read. Query dimensions can contain adapter, operation, inferred table, bounded normalized query, fingerprint, Active Record operation name, cache flag, connection role/shard, and a bounded source frame for slow sampled queries.
|
|
13
|
+
`Chronos::Core::SqlNormalizer` removes block/line comments, quoted literal values, numeric values, booleans, nulls, and repeated `IN` values before producing a SHA-256 fingerprint. Binds are never read. `SqlQueryAnalyzer` extracts bounded SELECT access columns. The optional `ActiveRecordQueryInspector` compares existing indexes and adds allowlisted statistics/plans without retaining raw SQL. Query dimensions can contain adapter, operation, inferred table, bounded normalized query, fingerprint, Active Record operation name, cache flag, connection role/shard, and a bounded source frame for slow sampled queries.
|
|
14
14
|
|
|
15
15
|
Local signals are intentionally heuristic:
|
|
16
16
|
|
|
@@ -18,7 +18,8 @@ Local signals are intentionally heuristic:
|
|
|
18
18
|
- `repeated_query` after the same fingerprint appears again in one trace;
|
|
19
19
|
- `possible_n_plus_one` once when the configured repetition threshold is reached;
|
|
20
20
|
- `long_transaction` for transaction-labelled SQL over its threshold;
|
|
21
|
-
- `connection_error
|
|
21
|
+
- `connection_error`, query/pool/lock timeout, constraint violation, and `deadlock` from bounded exception class names;
|
|
22
|
+
- index candidate/coverage and sequential-scan evidence from bounded query analysis.
|
|
22
23
|
|
|
23
24
|
The SaaS must confirm and correlate these signals. They are not proof of an N+1, deadlock, or application defect.
|
|
24
25
|
|
|
@@ -29,9 +30,11 @@ The SaaS must confirm and correlate these signals. They are not proof of an N+1,
|
|
|
29
30
|
- at most `apm_max_queries_per_request` fingerprints per trace;
|
|
30
31
|
- at most 19 configured histogram boundaries plus `+Inf`;
|
|
31
32
|
- at most `apm_batch_size` groups per payload, with a hard maximum of 50;
|
|
32
|
-
- trackers are removed when their request completes
|
|
33
|
+
- trackers are removed when their request completes or their idle TTL expires; aggregate drain preserves active trackers;
|
|
34
|
+
- at most 20 diagnostics and one representative bounded query analysis per metric group;
|
|
35
|
+
- at most `apm_query_inspection_max_queries` database inspections per Rails subscriber.
|
|
33
36
|
|
|
34
|
-
New groups beyond capacity are dropped and counted in `dropped_groups`. Existing groups continue accumulating. The state is process-local and is lost on restart.
|
|
37
|
+
New groups beyond capacity are dropped and counted in `dropped_groups`. Tracking loss is reported through `dropped_trace_trackers`, `expired_trace_trackers`, and `dropped_query_fingerprints`. Existing groups continue accumulating. The state is process-local and is lost on restart.
|
|
35
38
|
|
|
36
39
|
## Breakdown
|
|
37
40
|
|
|
@@ -51,6 +54,16 @@ Chronos.configure do |config|
|
|
|
51
54
|
config.apm_long_transaction_threshold_ms = 1000.0
|
|
52
55
|
config.apm_n_plus_one_threshold = 5
|
|
53
56
|
config.apm_histogram_buckets = [5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0]
|
|
57
|
+
config.apm_trace_ttl_seconds = 60.0
|
|
58
|
+
config.apm_query_analysis_enabled = true
|
|
59
|
+
config.apm_query_analysis_max_queries = 100
|
|
60
|
+
config.apm_query_inspection_enabled = false
|
|
61
|
+
config.apm_query_statistics_enabled = false
|
|
62
|
+
config.apm_query_plan_enabled = false
|
|
63
|
+
config.apm_query_inspection_min_duration_ms = 500.0
|
|
64
|
+
config.apm_query_inspection_max_queries = 20
|
|
65
|
+
config.apm_transaction_tracking_enabled = true
|
|
66
|
+
config.apm_transaction_max_connections = 100
|
|
54
67
|
end
|
|
55
68
|
```
|
|
56
69
|
|
|
@@ -4,7 +4,7 @@ Version `0.6.0.pre.1` starts the legacy jobs line with optional Sidekiq 4 and 5
|
|
|
4
4
|
|
|
5
5
|
```ruby
|
|
6
6
|
gem "sidekiq", "~> 5.0"
|
|
7
|
-
gem "chronos-ruby", "~> 1.
|
|
7
|
+
gem "chronos-ruby", "~> 1.1.0", :require => "chronos/sidekiq"
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
`chronos/sidekiq` installs middleware through the public `configure_client` and `configure_server` APIs. It does nothing when Sidekiq is unavailable, and the core gem never requires Sidekiq. Installation adds no Chronos thread or Redis/database connection per job; delivery continues through the agent's existing fixed worker pool.
|
|
@@ -1,22 +1,76 @@
|
|
|
1
1
|
# Monitoramento SQL
|
|
2
2
|
|
|
3
|
-
## Problema e
|
|
3
|
+
## Problema e limites
|
|
4
4
|
|
|
5
|
-
O monitoramento SQL mede
|
|
5
|
+
O monitoramento SQL mede padrões de acesso e duração sem transmitir SQL bruto ou binds. Ele identifica query lenta/repetida, possível N+1, transação longa, famílias de erro e candidatos a índice. Recomendações são evidências heurísticas: a gem não cria índices, não executa DDL, não usa `EXPLAIN ANALYZE` e não substitui revisão do DBA.
|
|
6
6
|
|
|
7
7
|
## Fluxo e classes
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart LR
|
|
11
|
+
AS[sql.active_record] --> N[SqlNormalizer]
|
|
12
|
+
N --> A[SqlQueryAnalyzer]
|
|
13
|
+
A --> M[ApmAggregator]
|
|
14
|
+
I[ActiveRecordQueryInspector opt-in] -->|índices, estatística, EXPLAIN allowlisted| A
|
|
15
|
+
M --> B[metric_batch sanitizado]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`Rails::NotificationsSubscriber` extrai campos permitidos. `Core::SqlNormalizer` remove comentários e literais, limita identificadores e calcula fingerprint SHA-256. `Core::SqlQueryAnalyzer` analisa somente o `SELECT` normalizado, extrai tabelas e colunas de igualdade, faixa, join e ordenação e produz candidatos limitados. `Rails::ActiveRecordQueryInspector` implementa a porta `Ports::QueryInspector` e, quando habilitado, compara o candidato com índices existentes, lê estimativa de linhas e obtém plano sem executar a consulta.
|
|
19
|
+
|
|
20
|
+
O inspector envia somente nomes limitados de tabela/índice/coluna, estimativa de linhas e nós de plano allowlisted: tipo, tabela, índice, custo e linhas estimadas. Predicados, filtros, valores, SQL original e mensagens de erro são descartados. Falhas viram apenas classe limitada em um diagnóstico `error`.
|
|
21
|
+
|
|
22
|
+
`Application::ApmAggregator` agrega no grupo da query:
|
|
23
|
+
|
|
24
|
+
- `severity_counts`: `error`, `warning`, `info` e `suggestion`;
|
|
25
|
+
- `diagnostics`: até 20 diagnósticos estáveis com código, categoria, evidência e contagem;
|
|
26
|
+
- `query_analysis`: uma análise representativa, preferindo a observação com inspeção mais rica;
|
|
27
|
+
- `signals`: contadores compatíveis de slow query, repetição, N+1, transação e erros;
|
|
28
|
+
- duração total/mínima/máxima/média, histograma e p50/p95/p99 aproximados.
|
|
29
|
+
|
|
30
|
+
## Severidades
|
|
31
|
+
|
|
32
|
+
| Severidade | Exemplos |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `error` | exceção SQL, conexão, pool/statement/lock timeout, deadlock, constraint e falha da inspeção |
|
|
35
|
+
| `warning` | query lenta, transação longa, possível N+1 e sequential scan observado |
|
|
36
|
+
| `info` | padrão normalizado analisado, query repetida e índice existente que cobre o padrão |
|
|
37
|
+
| `suggestion` | candidato de índice e uso de eager/batch loading para possível N+1 |
|
|
38
|
+
|
|
39
|
+
No projeto consumidor, cada item de `diagnostics` pode ser persistido como informação associada à métrica usando `code`, `severity`, `category`, `message`, `recommendation`, `evidence` e `count`. `code + severity + evidence.table + evidence.columns` forma a identidade agregada atual. O consumidor deve usar `severity` para apresentação e ordenação, não inferir severidade pelo texto. Campos desconhecidos devem ser preservados ou ignorados para manter compatibilidade aditiva.
|
|
10
40
|
|
|
11
|
-
|
|
41
|
+
Um candidato permanece `unverified` até o catálogo ser consultado. Ele se torna `covered` quando um índice possui o prefixo observado ou `missing` quando índices foram lidos e nenhum cobre esse prefixo. Seletividade, custo de escrita e distribuição de dados ainda precisam ser avaliados antes de qualquer migração.
|
|
12
42
|
|
|
13
|
-
|
|
43
|
+
## Configuração segura
|
|
14
44
|
|
|
15
45
|
```ruby
|
|
16
46
|
Chronos.configure do |config|
|
|
17
47
|
config.apm_slow_query_threshold_ms = 500.0
|
|
18
48
|
config.apm_n_plus_one_threshold = 5
|
|
49
|
+
config.apm_trace_ttl_seconds = 60.0
|
|
50
|
+
config.apm_query_analysis_enabled = true
|
|
51
|
+
config.apm_query_analysis_max_queries = 100
|
|
52
|
+
|
|
53
|
+
# Opt-in por adicionar consultas de somente leitura ao banco.
|
|
54
|
+
config.apm_query_inspection_enabled = true
|
|
55
|
+
config.apm_query_statistics_enabled = true
|
|
56
|
+
config.apm_query_plan_enabled = true
|
|
57
|
+
config.apm_query_inspection_min_duration_ms = 500.0
|
|
58
|
+
config.apm_query_inspection_max_queries = 20
|
|
19
59
|
end
|
|
20
60
|
```
|
|
21
61
|
|
|
22
|
-
|
|
62
|
+
A análise normalizada é ativada por padrão, não consulta o banco e é calculada uma vez por fingerprint até o limite do cache. Inspeção, estatísticas e plano são desativados por padrão. Cada fingerprint é inspecionado no máximo uma vez por subscriber até o limite configurado; uma inspeção posterior pode enriquecer a análise estática já armazenada. PostgreSQL usa `pg_class` e `EXPLAIN (FORMAT JSON)`; MySQL/Trilogy usa `information_schema` e `EXPLAIN`. Adapters não reconhecidos ainda recebem análise estática e índices caso exponham `connection.indexes`.
|
|
63
|
+
|
|
64
|
+
## Transações e N+1
|
|
65
|
+
|
|
66
|
+
O subscriber mede o intervalo aproximado entre os callbacks de `BEGIN`/`START TRANSACTION` e `COMMIT`/`ROLLBACK`, por identidade local da conexão. Savepoints não encerram a transação externa. Estados ociosos expiram e conexões rastreadas são limitadas. O valor não inclui o tempo gasto antes do callback de `BEGIN` nem depois do callback final.
|
|
67
|
+
|
|
68
|
+
Possível N+1 exige `SELECT` não cacheado, mesmo fingerprint, mesmo trace e repetição até o threshold. A sugestão recomenda eager loading ou carregamento em lote, mas o consumidor deve confirmar a semântica antes da alteração.
|
|
69
|
+
|
|
70
|
+
## Riscos e extensão
|
|
71
|
+
|
|
72
|
+
`EXPLAIN` ainda usa planner, conexão e locks leves de catálogo; por isso é opt-in e selecionado por duração. Não existe timeout portátil entre os adapters legacy, então habilite primeiro em staging e monitore o orçamento. Nomes de schema/tabela/coluna/índice podem revelar o domínio e precisam de avaliação LGPD.
|
|
73
|
+
|
|
74
|
+
Um inspector alternativo pode implementar `call(raw_sql, query, options)` e retornar `indexes`, `statistics`, `plan` e `errors` nos limites do contrato. Ele deve ser somente leitura e nunca devolver valores ou mensagens livres.
|
|
75
|
+
|
|
76
|
+
Os testes principais são `spec/unit/core/sql_query_analyzer_spec.rb`, `spec/unit/rails/active_record_query_inspector_spec.rb`, `spec/unit/application/apm_aggregator_spec.rb` e `spec/integration/rails_telemetry_delivery_spec.rb`. O exemplo executável está em `examples/plain-ruby/query_analysis.rb`.
|
|
@@ -6,6 +6,6 @@ Telemetry has the same `schema_version`, event ID, timestamps, project, environm
|
|
|
6
6
|
|
|
7
7
|
`Chronos.record_event` is the narrow integration entry point. Application code should prefer documented higher-level APIs; its payload is allowlisted by each bundled integration and then sanitized. Remote configuration can reduce or disable any locally enabled telemetry type but cannot enable a type excluded by local configuration.
|
|
8
8
|
|
|
9
|
-
Version 0.8 aggregates request, query, job, and enabled external HTTP observations into `metric_batch` events by default. Cache remains an individual event while contributing to an existing traced request breakdown. Dependencies use one separate event per agent. Set `apm_enabled = false` only when individual legacy telemetry is required for diagnostics.
|
|
9
|
+
Version 0.8 aggregates request, query, job, and enabled external HTTP observations into `metric_batch` events by default. Cache remains an individual event while contributing to an existing traced request breakdown. Dependencies use one separate event per agent. Set `apm_enabled = false` only when individual legacy telemetry is required for diagnostics. The agent emits histogram-derived approximate p50/p95/p99 while the SaaS may calculate more precise merged values; see [Essential APM aggregation](apm-aggregation.md).
|
|
10
10
|
|
|
11
11
|
Version 0.9 delivers deploy telemetry synchronously because deployment commands are short-lived. Every telemetry and exception envelope contains the fixed correlation fields documented in [Deploy tracking and release correlation](deploy-tracking.md).
|
data/docs/performance.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Performance
|
|
2
2
|
|
|
3
|
-
Performance is a functional requirement, but version 1.
|
|
3
|
+
Performance is a functional requirement, but version 1.1 makes no unverified speed claim.
|
|
4
4
|
|
|
5
5
|
Current controls:
|
|
6
6
|
|
|
@@ -18,9 +18,11 @@ Current controls:
|
|
|
18
18
|
- shutdown and flush have caller-controlled timeouts;
|
|
19
19
|
- request context and breadcrumbs have fixed structural and byte limits;
|
|
20
20
|
- Rack middleware never consumes request or response bodies.
|
|
21
|
-
- Rails subscribers copy only small allowlisted field sets and never
|
|
21
|
+
- Rails subscribers copy only small allowlisted field sets and never deliver raw SQL or job arguments.
|
|
22
22
|
- Sidekiq middleware creates no per-job thread or connection and bounds arguments, collections, nesting, strings, and tags before telemetry capture.
|
|
23
23
|
- APM group, trace, query-fingerprint, histogram, and batch counts are fixed; no APM timer thread is created.
|
|
24
|
+
- active trace trackers survive aggregate drains but expire after `apm_trace_ttl_seconds`; query inspections and transaction connections have independent fixed caps;
|
|
25
|
+
- normalized query analysis is bounded and database inspection is disabled by default; opt-in index/statistics/plan inspection adds database round trips only once per selected fingerprint;
|
|
24
26
|
- outbound HTTP instrumentation uses two clock reads and bounded metadata without body/header traversal;
|
|
25
27
|
- cache normalization is bounded and SHA-256 runs only when explicitly enabled;
|
|
26
28
|
- dependency inventory runs at most once per agent and is capped at 200 loaded specs.
|
|
@@ -29,7 +31,21 @@ Current controls:
|
|
|
29
31
|
|
|
30
32
|
Run the scripts under `benchmarks/` and record Ruby version, operating system, CPU, warmup, iteration count, median, and dispersion before publishing results. `benchmarks/filtering.rb` measures privacy filtering, `benchmarks/retry_backlog.rb` measures fixed-memory outage behavior, `benchmarks/request_overhead.rb` compares Rack-protocol calls, and `benchmarks/rails_notifications.rb` isolates subscriber normalization overhead.
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
`benchmarks/query_analysis.rb` compares normalization alone with normalization plus bounded static analysis. It deliberately excludes database inspection because catalog and planner cost must be measured against the actual adapter, schema, statistics, and database host before production enablement.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ITERATIONS=100000 WARMUP=5000 bundle exec ruby benchmarks/query_analysis.rb
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Version 1.1.0 release gates
|
|
41
|
+
|
|
42
|
+
Version 1.1 adds a release-gate benchmark for normalized SQL analysis. Static analysis is calculated once per fingerprint and cached under `apm_query_analysis_max_queries`; database inspection remains disabled in the benchmark because its cost depends on the actual adapter, schema, statistics, network, and database host.
|
|
43
|
+
|
|
44
|
+
The tag workflow runs query analysis with 1,000 warmup and 10,000 measured iterations, then repeats the existing Rack comparison and 500-event fake-endpoint privacy/load gate. Local preparation evidence is recorded in [Version 1.1 readiness](release-1.1-readiness.md). Results are environment-specific and are not a cross-runtime performance promise.
|
|
45
|
+
|
|
46
|
+
The local Ruby 2.2.10 preparation run measured 453.981 µs per normalization, 594.862 µs for the first static analysis of a fingerprint, 43.946 µs median incremental Rack work, and 500/500 fake-endpoint deliveries at 378.15 events/s. Static analysis is cached; the 594.862 µs value is not paid for every repeated fingerprint.
|
|
47
|
+
|
|
48
|
+
## Historical version 1.0.0 release gates
|
|
33
49
|
|
|
34
50
|
`benchmarks/comparative.rb` compares the same successful Rack fixture without and with Chronos instrumentation. It performs configurable warmup, at least three samples, and reports median plus median absolute deviation. `benchmarks/fake_endpoint_load.rb` sends asynchronous exception events to a local TCP endpoint, verifies the v1 schema marker, ensures the secret key is absent from every payload, and fails on loss, rejection, invalid payload, or timeout.
|
|
35
51
|
|