2
2
3
3
module SafePgMigrations
4
4
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
26
17
27
18
%i[
28
19
add_column remove_column add_foreign_key remove_foreign_key change_column_default change_column_null create_table
@@ -34,14 +25,40 @@ module BlockingActivityLogger
34
25
35
26
private
36
27
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
+
37
54
def log_blocking_queries
38
55
delay_before_logging =
39
56
SafePgMigrations . config . safe_timeout - SafePgMigrations . config . blocking_activity_logger_margin
40
57
41
58
blocking_queries_retriever_thread =
42
59
Thread . new do
43
60
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 )
45
62
end
46
63
47
64
yield
@@ -66,7 +83,7 @@ def log_blocking_queries
66
83
"Statement was being blocked by the following #{ 'query' . pluralize ( queries . size ) } :" , true
67
84
)
68
85
SafePgMigrations . say '' , true
69
- queries . each { | query , start_time | SafePgMigrations . say " #{ format_start_time start_time } : #{ query } " , true }
86
+ output_blocking_queries ( queries )
70
87
SafePgMigrations . say (
71
88
'Beware, some of those queries might run in a transaction. In this case the locking query might be ' \
72
89
'located elsewhere in the transaction' ,
@@ -78,6 +95,22 @@ def log_blocking_queries
78
95
raise
79
96
end
80
97
98
+ def output_blocking_queries ( queries )
99
+ if SafePgMigrations . config . blocking_activity_logger_verbose
100
+ queries . each { |query , start_time | SafePgMigrations . say "#{ format_start_time start_time } : #{ query } " , true }
101
+ else
102
+ queries . each do |start_time , locktype , mode , pid , transactionid |
103
+ SafePgMigrations . say (
104
+ "#{ format_start_time ( start_time ) } : lock type: #{ locktype || 'null' } , " \
105
+ "lock mode: #{ mode || 'null' } , " \
106
+ "lock pid: #{ pid || 'null' } , " \
107
+ "lock transactionid: #{ transactionid || 'null' } " ,
108
+ true
109
+ )
110
+ end
111
+ end
112
+ end
113
+
81
114
def format_start_time ( start_time , reference_time = Time . now )
82
115
duration = ( reference_time - start_time ) . round
83
116
"transaction started #{ duration } #{ 'second' . pluralize ( duration ) } ago"
0 commit comments