22
33module SafePgMigrations
44 module BlockingActivityLogger
5- SELECT_BLOCKING_QUERIES_SQL = <<~SQL . squish
6- SELECT blocking_activity.query, blocked_activity.xact_start as start
7- FROM pg_catalog.pg_locks blocked_locks
8- JOIN pg_catalog.pg_stat_activity blocked_activity
9- ON blocked_activity.pid = blocked_locks.pid
10- JOIN pg_catalog.pg_locks blocking_locks
11- ON blocking_locks.locktype = blocked_locks.locktype
12- AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
13- AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
14- AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
15- AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
16- AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
17- AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
18- AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
19- AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
20- AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
21- AND blocking_locks.pid != blocked_locks.pid
22- JOIN pg_catalog.pg_stat_activity blocking_activity
23- ON blocking_activity.pid = blocking_locks.pid
24- WHERE blocked_locks.pid = %d
25- SQL
5+ FILTERED_COLUMNS = %w(
6+ blocked_activity.xact_start
7+ blocked_locks.locktype
8+ blocked_locks.mode
9+ blocking_activity.pid
10+ blocked_locks.transactionid
11+ ) . freeze
12+
13+ VERBOSE_COLUMNS = %w(
14+ blocking_activity.query
15+ blocked_activity.xact_start
16+ ) . freeze
2617
2718 %i[
2819 add_column remove_column add_foreign_key remove_foreign_key change_column_default change_column_null create_table
@@ -34,14 +25,40 @@ module BlockingActivityLogger
3425
3526 private
3627
28+ def select_blocking_queries_sql
29+ columns = SafePgMigrations . config . blocking_activity_logger_verbose ? VERBOSE_COLUMNS : FILTERED_COLUMNS
30+
31+ <<~SQL . squish
32+ SELECT #{ columns . join ( ', ' ) }
33+ FROM pg_catalog.pg_locks blocked_locks
34+ JOIN pg_catalog.pg_stat_activity blocked_activity
35+ ON blocked_activity.pid = blocked_locks.pid
36+ JOIN pg_catalog.pg_locks blocking_locks
37+ ON blocking_locks.locktype = blocked_locks.locktype
38+ AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
39+ AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
40+ AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
41+ AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
42+ AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
43+ AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
44+ AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
45+ AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
46+ AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
47+ AND blocking_locks.pid != blocked_locks.pid
48+ JOIN pg_catalog.pg_stat_activity blocking_activity
49+ ON blocking_activity.pid = blocking_locks.pid
50+ WHERE blocked_locks.pid = %d
51+ SQL
52+ end
53+
3754 def log_blocking_queries
3855 delay_before_logging =
3956 SafePgMigrations . config . safe_timeout - SafePgMigrations . config . blocking_activity_logger_margin
4057
4158 blocking_queries_retriever_thread =
4259 Thread . new do
4360 sleep delay_before_logging
44- SafePgMigrations . alternate_connection . query ( SELECT_BLOCKING_QUERIES_SQL % raw_connection . backend_pid )
61+ SafePgMigrations . alternate_connection . query ( select_blocking_queries_sql % raw_connection . backend_pid )
4562 end
4663
4764 yield
@@ -66,7 +83,7 @@ def log_blocking_queries
6683 "Statement was being blocked by the following #{ 'query' . pluralize ( queries . size ) } :" , true
6784 )
6885 SafePgMigrations . say '' , true
69- queries . each { | query , start_time | SafePgMigrations . say " #{ format_start_time start_time } : #{ query } " , true }
86+ output_blocking_queries ( queries )
7087 SafePgMigrations . say (
7188 'Beware, some of those queries might run in a transaction. In this case the locking query might be ' \
7289 'located elsewhere in the transaction' ,
@@ -78,6 +95,22 @@ def log_blocking_queries
7895 raise
7996 end
8097
98+ def output_blocking_queries ( queries )
99+ if SafePgMigrations . config . blocking_activity_logger_verbose
100+ queries . each do |query , start_time |
101+ SafePgMigrations . say "#{ format_start_time start_time } : #{ query } " , true
102+ end
103+ else
104+ queries . each do |start_time , locktype , mode , pid , transactionid |
105+ SafePgMigrations . say format_start_time ( start_time ) , true
106+ SafePgMigrations . say "lock type: #{ locktype || 'null' } "
107+ SafePgMigrations . say "lock mode: #{ mode || 'null' } "
108+ SafePgMigrations . say "lock pid: #{ pid || 'null' } "
109+ SafePgMigrations . say "lock transactionid: #{ transactionid || 'null' } "
110+ end
111+ end
112+ end
113+
81114 def format_start_time ( start_time , reference_time = Time . now )
82115 duration = ( reference_time - start_time ) . round
83116 "transaction started #{ duration } #{ 'second' . pluralize ( duration ) } ago"
0 commit comments