Class: DeadBro::SqlTrackingMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/dead_bro/sql_tracking_middleware.rb

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ SqlTrackingMiddleware

Returns a new instance of SqlTrackingMiddleware.



5
6
7
# File 'lib/dead_bro/sql_tracking_middleware.rb', line 5

def initialize(app)
  @app = app
end

Instance Method Details

#call(env) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/dead_bro/sql_tracking_middleware.rb', line 9

def call(env)
  return @app.call(env) if DeadBro.configuration.skip_tracking?

  # Capture rack entry time before any setup so middleware overhead is accurately measured.
  rack_entry = Time.now
  Thread.current[DeadBro::TRACKING_START_TIME_KEY] = rack_entry

  # Queue time: gap between when the upstream proxy accepted the connection and when a Rack
  # worker picked it up. Heroku sets X-Request-Start as "t=<microseconds>"; nginx typically
  # uses "t=<seconds.ms>". Both are parsed below.
  Thread.current[:dead_bro_queue_duration_ms] = parse_queue_start(env, rack_entry)

  # Clear logs for this request
  DeadBro.logger.clear

  # Start SQL tracking for this request
  if defined?(DeadBro::SqlSubscriber)
    DeadBro::SqlSubscriber.start_request_tracking
  end

  # Start cache tracking for this request
  if defined?(DeadBro::CacheSubscriber)
    DeadBro::CacheSubscriber.start_request_tracking
  end

  # Start Redis tracking for this request
  if defined?(DeadBro::RedisSubscriber)
    DeadBro::RedisSubscriber.start_request_tracking
  end

  # Start view rendering tracking for this request
  if defined?(DeadBro::ViewRenderingSubscriber)
    DeadBro::ViewRenderingSubscriber.start_request_tracking
  end

  # Start lightweight memory tracking for this request
  if defined?(DeadBro::LightweightMemoryTracker)
    DeadBro::LightweightMemoryTracker.start_request_tracking
  end

  # Decide once whether this request pays for heavy allocation tracking
  # (flag + per-request sampling). Cache the decision so the matching stop
  # in Subscriber agrees with this start.
  alloc_active = DeadBro.configuration.allocation_tracking_active?
  Thread.current[:dead_bro_alloc_active] = alloc_active

  # Start detailed memory + allocation-source tracking when active
  if alloc_active
    DeadBro::MemoryTrackingSubscriber.start_request_tracking if defined?(DeadBro::MemoryTrackingSubscriber)
    DeadBro::AllocationSourceSampler.start if defined?(DeadBro::AllocationSourceSampler)
  end

  # Start Elasticsearch tracking for this request
  if defined?(DeadBro::ElasticsearchSubscriber)
    DeadBro::ElasticsearchSubscriber.start_request_tracking
  end

  # Start DB connection pool wait tracking
  if defined?(DeadBro::DbConnectionSubscriber)
    DeadBro::DbConnectionSubscriber.start_request_tracking
  end

  # Start GC pressure tracking — snapshot before any app code runs
  DeadBro::GcTracker.start_request_tracking if defined?(DeadBro::GcTracker)

  # Start per-phase allocation attribution (~0.1ms; under memory tracking)
  if DeadBro.configuration.memory_tracking_enabled && defined?(DeadBro::MemoryPhaseTracker)
    DeadBro::MemoryPhaseTracker.start_request_tracking
  end

  # Start AR object instantiation counting for this request
  DeadBro::ArObjectTracker.start_request_tracking if defined?(DeadBro::ArObjectTracker)

  # Start CPU time tracking for this request (thread-local clock)
  DeadBro::CpuTracker.start_request_tracking if defined?(DeadBro::CpuTracker)

  # Start outgoing HTTP accumulation for this request
  Thread.current[:dead_bro_http_events] = []

  @app.call(env)
ensure
  # Clean up thread-local storage
  if defined?(DeadBro::SqlSubscriber)
    Thread.current[:dead_bro_sql_queries]
    Thread.current[:dead_bro_sql_queries] = nil
  end

  if defined?(DeadBro::CacheSubscriber)
    Thread.current[:dead_bro_cache_events]
    Thread.current[:dead_bro_cache_events] = nil
  end

  if defined?(DeadBro::RedisSubscriber)
    Thread.current[:dead_bro_redis_events]
    Thread.current[:dead_bro_redis_events] = nil
  end

  if defined?(DeadBro::ViewRenderingSubscriber)
    Thread.current[:dead_bro_view_events]
    Thread.current[:dead_bro_view_events] = nil
  end

  if defined?(DeadBro::LightweightMemoryTracker)
    Thread.current[:dead_bro_lightweight_memory]
    Thread.current[:dead_bro_lightweight_memory] = nil
  end

  # Clean up HTTP events, ES events, DB connection tracking, and tracking start time
  Thread.current[:dead_bro_elasticsearch_events] = nil
  Thread.current[:dead_bro_http_events] = nil
  Thread.current[:dead_bro_queue_duration_ms] = nil
  DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
  Thread.current[DeadBro::GcTracker::THREAD_KEY] = nil if defined?(DeadBro::GcTracker)
  # Bypass stop_request_tracking intentionally — cleanup only, no return value needed here.
  Thread.current[DeadBro::ArObjectTracker::THREAD_KEY] = nil if defined?(DeadBro::ArObjectTracker)
  Thread.current[DeadBro::CpuTracker::THREAD_KEY] = nil if defined?(DeadBro::CpuTracker)
  Thread.current[DeadBro::MemoryPhaseTracker::THREAD_KEY] = nil if defined?(DeadBro::MemoryPhaseTracker)
  # Safety net: ensure allocation tracing is never left running across
  # requests (Subscriber normally stops it after analyzing).
  if Thread.current[:dead_bro_alloc_active]
    DeadBro::AllocationSourceSampler.stop if defined?(DeadBro::AllocationSourceSampler)
  end
  Thread.current[:dead_bro_alloc_active] = nil
  Thread.current[DeadBro::TRACKING_START_TIME_KEY] = nil
end