dead_bro 0.2.14 → 0.2.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f7618eda689dd41bbb5c71cdff40619e5857a71b31a182f234064d89197d79c
4
- data.tar.gz: fbcac3f8c189834690a5a75e013f07cec20044f4e74285139aa22f66ae0cf65d
3
+ metadata.gz: 2251b88befeee4d2b7668bc504cc99d94cd154dd4539258545c1084e7a388709
4
+ data.tar.gz: 10141477e9697b91d95290ccfc695abb304e8dfbdfb526f95646269be03be0ef
5
5
  SHA512:
6
- metadata.gz: 68864135e9fd51e65293ee369f1002e6f37341627fd674d570781fa163dd5b2345a4e7421356f49bc2bc00c1b8f37946b7fe9d31ff0370a6781f892392c7ec5d
7
- data.tar.gz: 5e72422ba6297af9ef9a521e675d832dd94a0f81783ae964be53ecdd16eaa96dd4bc55448baceb697ea1b0b4bfd024090009d43dd87a3893f197df607cc08fd4
6
+ metadata.gz: 39ec9243a8792016f7cf9bf5f8d81856c121365b5cebef91ef7d318f642e7480a66b32a92b63ec944ee3aebb9e92ffdfc27c9e284e41da7daa081661e893b38a
7
+ data.tar.gz: 519f6df31fb8dc567a6974052e58787098021078514eaa2877a2f582651c5a5fec316f4da58a625a4b6d00eed7fae614fe5dd79f1da17ae2d4d7227ce8f7061b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.14] - 2026-05-23
4
+
5
+ ### Added
6
+ - `DbConnectionSubscriber`: measures the time threads spend waiting to acquire a connection from the ActiveRecord connection pool and counts checkouts per request. Uses `prepend` on `ActiveRecord::ConnectionAdapters::ConnectionPool#checkout` so the overhead is minimal and the instrumentation is invisible to application code. `db_connection_wait_ms` and `db_connection_checkouts` are included in both request and job payloads.
7
+ - Queue duration tracking for web requests via `X-Request-Start` / `X-Queue-Start` headers. Parses both the Heroku microsecond format (`t=<µs>`) and the nginx seconds format (`t=<s.ms>`), and applies a 60 s clock-skew cap so a misconfigured proxy timestamp cannot produce a nonsensical value.
8
+ - Queue duration tracking for background jobs: time from `job.enqueued_at` to when `perform` begins is reported as `queue_duration_ms` in every job payload.
9
+ - `RedisSubscriber`: prepend-based instrumentation on `Redis::Client` that records every individual command, pipeline, and `MULTI`/`EXEC` block with command name, key, duration in ms, and database index. Falls back to an `ActiveSupport::Notifications` subscription for `redis.*` events emitted by other libraries. Capped at 1 000 events per request to bound memory growth.
10
+ - DB connection tracking wired into background jobs: `DbConnectionSubscriber` is started and stopped around job execution in both the normal completion path and the exception handler fallback path, with cleanup in `drain_job_tracking`.
11
+
3
12
  ## [0.1.0] - 2025-08-28
4
13
 
5
14
  - Initial release
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeadBro
4
+ module GcTracker
5
+ THREAD_KEY = :dead_bro_gc_start
6
+
7
+ def self.start_request_tracking
8
+ Thread.current[THREAD_KEY] = snapshot
9
+ end
10
+
11
+ def self.stop_request_tracking
12
+ before = Thread.current[THREAD_KEY]
13
+ return {} if before.nil? || before.empty?
14
+ diff(before, snapshot)
15
+ ensure
16
+ Thread.current[THREAD_KEY] = nil
17
+ end
18
+
19
+ def self.snapshot
20
+ return {} unless defined?(GC) && GC.respond_to?(:stat)
21
+ stat = GC.stat
22
+ {
23
+ minor_gc_count: stat[:minor_gc_count] || 0,
24
+ major_gc_count: stat[:major_gc_count] || 0,
25
+ total_allocated_objects: stat[:total_allocated_objects] || 0,
26
+ gc_time_ns: GC.respond_to?(:total_time) ? GC.total_time : nil
27
+ }
28
+ rescue
29
+ {}
30
+ end
31
+
32
+ def self.diff(before, after)
33
+ return {} if before.empty? || after.empty?
34
+ gc_time_ms = if before[:gc_time_ns] && after[:gc_time_ns]
35
+ ((after[:gc_time_ns] - before[:gc_time_ns]) / 1_000_000.0).round(3)
36
+ end
37
+ {
38
+ minor_gc_runs: (after[:minor_gc_count] || 0) - (before[:minor_gc_count] || 0),
39
+ major_gc_runs: (after[:major_gc_count] || 0) - (before[:major_gc_count] || 0),
40
+ allocated_objects: (after[:total_allocated_objects] || 0) - (before[:total_allocated_objects] || 0),
41
+ gc_time_ms: gc_time_ms
42
+ }
43
+ rescue
44
+ {}
45
+ end
46
+ end
47
+ end
@@ -12,6 +12,12 @@ module DeadBro
12
12
  JOB_EXCEPTION_EVENT_NAME = "exception.active_job"
13
13
 
14
14
  def self.subscribe!(client: Client.new)
15
+ # Snap GC state before the job runs so stop_request_tracking gets a valid diff
16
+ ActiveSupport::Notifications.subscribe("perform_start.active_job") do |_name, _started, _finished, _unique_id, _data|
17
+ DeadBro::GcTracker.start_request_tracking if defined?(DeadBro::GcTracker)
18
+ rescue
19
+ end
20
+
15
21
  # Track job execution
16
22
  ActiveSupport::Notifications.subscribe(JOB_EVENT_NAME) do |name, started, finished, _unique_id, data|
17
23
  begin
@@ -62,6 +68,7 @@ module DeadBro
62
68
  # Get SQL queries executed during this job
63
69
  sql_queries = DeadBro::SqlSubscriber.stop_request_tracking
64
70
  db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
71
+ gc_pressure = defined?(DeadBro::GcTracker) ? DeadBro::GcTracker.stop_request_tracking : {}
65
72
 
66
73
  # Stop memory tracking and get collected memory data
67
74
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
@@ -100,6 +107,7 @@ module DeadBro
100
107
  queue_duration_ms: queue_duration_ms,
101
108
  db_connection_wait_ms: db_connection_stats[:wait_ms],
102
109
  db_connection_checkouts: db_connection_stats[:checkouts],
110
+ gc_pressure: gc_pressure,
103
111
  status: "completed",
104
112
  sql_queries: sql_queries,
105
113
  rails_env: DeadBro.env,
@@ -153,6 +161,7 @@ module DeadBro
153
161
  # Get SQL queries executed during this job
154
162
  sql_queries = DeadBro::SqlSubscriber.stop_request_tracking
155
163
  db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
164
+ gc_pressure = defined?(DeadBro::GcTracker) ? DeadBro::GcTracker.stop_request_tracking : {}
156
165
 
157
166
  # Stop memory tracking and get collected memory data
158
167
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
@@ -191,6 +200,7 @@ module DeadBro
191
200
  queue_duration_ms: queue_duration_ms,
192
201
  db_connection_wait_ms: db_connection_stats[:wait_ms],
193
202
  db_connection_checkouts: db_connection_stats[:checkouts],
203
+ gc_pressure: gc_pressure,
194
204
  status: "failed",
195
205
  sql_queries: sql_queries,
196
206
  exception_class: exception&.class&.name,
@@ -217,6 +227,7 @@ module DeadBro
217
227
  def self.drain_job_tracking
218
228
  DeadBro::SqlSubscriber.stop_request_tracking if defined?(DeadBro::SqlSubscriber)
219
229
  DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
230
+ DeadBro::GcTracker.stop_request_tracking if defined?(DeadBro::GcTracker)
220
231
  DeadBro::LightweightMemoryTracker.stop_request_tracking if defined?(DeadBro::LightweightMemoryTracker)
221
232
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
222
233
  DeadBro::MemoryTrackingSubscriber.stop_request_tracking
@@ -61,6 +61,9 @@ module DeadBro
61
61
  DeadBro::DbConnectionSubscriber.start_request_tracking
62
62
  end
63
63
 
64
+ # Start GC pressure tracking — snapshot before any app code runs
65
+ DeadBro::GcTracker.start_request_tracking if defined?(DeadBro::GcTracker)
66
+
64
67
  # Start outgoing HTTP accumulation for this request
65
68
  Thread.current[:dead_bro_http_events] = []
66
69
 
@@ -97,6 +100,7 @@ module DeadBro
97
100
  Thread.current[:dead_bro_http_events] = nil
98
101
  Thread.current[:dead_bro_queue_duration_ms] = nil
99
102
  DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
103
+ Thread.current[DeadBro::GcTracker::THREAD_KEY] = nil if defined?(DeadBro::GcTracker)
100
104
  Thread.current[DeadBro::TRACKING_START_TIME_KEY] = nil
101
105
  end
102
106
 
@@ -65,6 +65,9 @@ module DeadBro
65
65
  # Stop DB connection pool wait tracking
66
66
  db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
67
67
 
68
+ # Stop GC pressure tracking — diff minor/major runs, objects allocated, GC time
69
+ gc_pressure = defined?(DeadBro::GcTracker) ? DeadBro::GcTracker.stop_request_tracking : {}
70
+
68
71
  # Stop view rendering tracking and get collected view events
69
72
  view_events = DeadBro::ViewRenderingSubscriber.stop_request_tracking
70
73
  view_performance = DeadBro::ViewRenderingSubscriber.analyze_view_performance(view_events)
@@ -178,6 +181,7 @@ module DeadBro
178
181
  queue_duration_ms: Thread.current[:dead_bro_queue_duration_ms],
179
182
  db_connection_wait_ms: db_connection_stats[:wait_ms],
180
183
  db_connection_checkouts: db_connection_stats[:checkouts],
184
+ gc_pressure: gc_pressure,
181
185
  logs: DeadBro.logger.logs
182
186
  }
183
187
  client.post_metric(event_name: name, payload: payload)
@@ -199,6 +203,7 @@ module DeadBro
199
203
  end
200
204
  Thread.current[:dead_bro_http_events] = nil
201
205
  DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
206
+ DeadBro::GcTracker.stop_request_tracking if defined?(DeadBro::GcTracker)
202
207
  rescue
203
208
  # Best effort — draining must never raise from the notifications callback.
204
209
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeadBro
4
- VERSION = "0.2.14"
4
+ VERSION = "0.2.15"
5
5
  end
data/lib/dead_bro.rb CHANGED
@@ -18,6 +18,7 @@ module DeadBro
18
18
  autoload :MemoryTrackingSubscriber, "dead_bro/memory_tracking_subscriber"
19
19
  autoload :MemoryLeakDetector, "dead_bro/memory_leak_detector"
20
20
  autoload :LightweightMemoryTracker, "dead_bro/lightweight_memory_tracker"
21
+ autoload :GcTracker, "dead_bro/gc_tracker"
21
22
  autoload :MemoryHelpers, "dead_bro/memory_helpers"
22
23
  autoload :JobSubscriber, "dead_bro/job_subscriber"
23
24
  autoload :JobSqlTrackingMiddleware, "dead_bro/job_sql_tracking_middleware"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dead_bro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.14
4
+ version: 0.2.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emanuel Comsa
@@ -37,6 +37,7 @@ files:
37
37
  - lib/dead_bro/dispatcher.rb
38
38
  - lib/dead_bro/elasticsearch_subscriber.rb
39
39
  - lib/dead_bro/error_middleware.rb
40
+ - lib/dead_bro/gc_tracker.rb
40
41
  - lib/dead_bro/http_instrumentation.rb
41
42
  - lib/dead_bro/job_sql_tracking_middleware.rb
42
43
  - lib/dead_bro/job_subscriber.rb