dead_bro 0.2.0 → 0.2.1
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/README.md +4 -4
- data/lib/dead_bro/configuration.rb +1 -1
- data/lib/dead_bro/sql_subscriber.rb +48 -5
- data/lib/dead_bro/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fb42d69f24067aad16e5b9287d018590cd4876d5d37d40c4903d4216af721054
|
|
4
|
+
data.tar.gz: 27ce42b49dd15c548cebbbf8f95cb294c0479da8a3c41a1a8413e855fe929908
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: daeeb6923d46b072535cff1e03d89b7f9cb7f5d3373bfdcb36c36f876c3460dbc612885c65940f2b65f99ce3929cefb41b9db1204ce22e8601eee9987b22a73c
|
|
7
|
+
data.tar.gz: 59335da8149d74c7e1e00be6ba7dc700a09eb261bb4e5322fd22914c57f57f468ee34733973ab08125c79072c9ff59bba42990bd9cc5721aff5484e38fea3243
|
data/README.md
CHANGED
|
@@ -22,9 +22,9 @@ You can set via an initializer:
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
```ruby
|
|
25
|
-
DeadBro.configure do |
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
DeadBro.configure do |config|
|
|
26
|
+
config.api_key = ENV["DEAD_BRO_API_KEY"]
|
|
27
|
+
config.enabled = true
|
|
28
28
|
end
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -180,7 +180,7 @@ DeadBro can automatically run `EXPLAIN ANALYZE` on slow SQL queries to help you
|
|
|
180
180
|
|
|
181
181
|
```ruby
|
|
182
182
|
DeadBro.configure do |config|
|
|
183
|
-
config.api_key = ENV['
|
|
183
|
+
config.api_key = ENV['DEAD_BRO_API_KEY']
|
|
184
184
|
config.enabled = true
|
|
185
185
|
|
|
186
186
|
# Enable EXPLAIN ANALYZE for queries slower than 500ms
|
|
@@ -58,7 +58,9 @@ module DeadBro
|
|
|
58
58
|
if should_explain_query?(duration_ms, original_sql)
|
|
59
59
|
# Store reference to query_info so we can update it when EXPLAIN completes
|
|
60
60
|
query_info[:explain_plan] = nil # Placeholder
|
|
61
|
-
|
|
61
|
+
# Capture binds if available (type_casted_binds is preferred as they are ready for quoting)
|
|
62
|
+
binds = data[:type_casted_binds] || data[:binds]
|
|
63
|
+
start_explain_analyze_background(original_sql, data[:connection_id], query_info, binds)
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
# Add to thread-local storage
|
|
@@ -142,7 +144,7 @@ module DeadBro
|
|
|
142
144
|
true
|
|
143
145
|
end
|
|
144
146
|
|
|
145
|
-
def self.start_explain_analyze_background(sql, connection_id, query_info)
|
|
147
|
+
def self.start_explain_analyze_background(sql, connection_id, query_info, binds = nil)
|
|
146
148
|
return unless defined?(ActiveRecord)
|
|
147
149
|
return unless ActiveRecord::Base.respond_to?(:connection)
|
|
148
150
|
|
|
@@ -160,8 +162,11 @@ module DeadBro
|
|
|
160
162
|
connection = ActiveRecord::Base.connection
|
|
161
163
|
end
|
|
162
164
|
|
|
165
|
+
# Interpolate binds if present to ensure EXPLAIN works with placeholders
|
|
166
|
+
final_sql = interpolate_sql_with_binds(sql, binds, connection)
|
|
167
|
+
|
|
163
168
|
# Build EXPLAIN query based on database adapter
|
|
164
|
-
explain_sql = build_explain_query(
|
|
169
|
+
explain_sql = build_explain_query(final_sql, connection)
|
|
165
170
|
|
|
166
171
|
# Execute the EXPLAIN query
|
|
167
172
|
# For PostgreSQL, use select_all which returns ActiveRecord::Result
|
|
@@ -182,10 +187,8 @@ module DeadBro
|
|
|
182
187
|
# This updates the hash that's already in the queries array
|
|
183
188
|
if explain_plan && !explain_plan.to_s.strip.empty?
|
|
184
189
|
query_info[:explain_plan] = explain_plan
|
|
185
|
-
append_log_to_thread(main_thread, :debug, "Captured EXPLAIN ANALYZE for slow query (#{query_info[:duration_ms]}ms): #{explain_plan[0..1000]}...")
|
|
186
190
|
else
|
|
187
191
|
query_info[:explain_plan] = nil
|
|
188
|
-
append_log_to_thread(main_thread, :debug, "EXPLAIN ANALYZE returned empty result. Result type: #{result.class}, Result: #{result.inspect[0..200]}")
|
|
189
192
|
end
|
|
190
193
|
rescue => e
|
|
191
194
|
# Silently fail - don't let EXPLAIN break the application
|
|
@@ -267,6 +270,46 @@ module DeadBro
|
|
|
267
270
|
end
|
|
268
271
|
end
|
|
269
272
|
|
|
273
|
+
def self.interpolate_sql_with_binds(sql, binds, connection)
|
|
274
|
+
return sql if binds.nil? || binds.empty?
|
|
275
|
+
return sql unless connection
|
|
276
|
+
|
|
277
|
+
interpolated_sql = sql.dup
|
|
278
|
+
|
|
279
|
+
# Handle $1, $2 style placeholders (PostgreSQL)
|
|
280
|
+
if interpolated_sql.include?("$1")
|
|
281
|
+
binds.each_with_index do |val, index|
|
|
282
|
+
# Get value from bind param (handle both raw values and ActiveRecord::Relation::QueryAttribute)
|
|
283
|
+
value = val
|
|
284
|
+
if val.respond_to?(:value_for_database)
|
|
285
|
+
value = val.value_for_database
|
|
286
|
+
elsif val.respond_to?(:value)
|
|
287
|
+
value = val.value
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
quoted_value = connection.quote(value)
|
|
291
|
+
interpolated_sql = interpolated_sql.gsub("$#{index + 1}", quoted_value)
|
|
292
|
+
end
|
|
293
|
+
elsif interpolated_sql.include?("?")
|
|
294
|
+
# Handle ? style placeholders (MySQL, SQLite)
|
|
295
|
+
# We need to replace ? one by one in order
|
|
296
|
+
binds.each do |val|
|
|
297
|
+
# Get value from bind param
|
|
298
|
+
value = val
|
|
299
|
+
if val.respond_to?(:value_for_database)
|
|
300
|
+
value = val.value_for_database
|
|
301
|
+
elsif val.respond_to?(:value)
|
|
302
|
+
value = val.value
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
quoted_value = connection.quote(value)
|
|
306
|
+
interpolated_sql = interpolated_sql.sub("?", quoted_value)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
interpolated_sql
|
|
311
|
+
end
|
|
312
|
+
|
|
270
313
|
def self.format_explain_result(result, connection)
|
|
271
314
|
adapter_name = connection.adapter_name.downcase
|
|
272
315
|
|
data/lib/dead_bro/version.rb
CHANGED