Merge ~cjwatson/launchpad:update-pgkill-scripts into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: b166ec5666414d420f38d2047164282dafe2c6b8
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:update-pgkill-scripts
Merge into: launchpad:master
Diff against target: 143 lines (+34/-29)
2 files modified
utilities/pgkillactive.py (+12/-8)
utilities/pgkillidle.py (+22/-21)
Reviewer Review Type Date Requested Status
Ines Almeida Approve
Review via email: mp+448725@code.launchpad.net

Commit message

Update pgkillactive and pgkillidle scripts

Description of the change

Similar versions of these scripts exist in lp:losa-db-scripts and lp:postgresql-charm, and have been a bit more actively maintained there. However, it's still useful to have copies of these in the Launchpad tree, since the simplest way to migrate some current cron jobs from production will be to run these as part of the `launchpad-admin` charm.

Sync our versions up more closely with those maintained elsewhere, making the following changes:

 * Exclude the current process from being killed.
 * Terminate the PostgreSQL backend process remotely using `pg_terminate_backend`, rather than relying on running on the database host itself.
 * Make the `pgkillidle -u` option match either the username or the application name.
 * Update `pgkillidle` for `pg_stat_activity` changes in PostgreSQL >= 9.2.
 * Make `pgkillidle` print a little more information on the process being killed.
 * Minor changes to docstrings and help text.

To post a comment you must log in.
Revision history for this message
Ines Almeida (ines-almeida) wrote :

Looks good

review: Approve
Revision history for this message
Colin Watson (cjwatson) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/utilities/pgkillactive.py b/utilities/pgkillactive.py
2index c8e5e44..803e3d1 100755
3--- a/utilities/pgkillactive.py
4+++ b/utilities/pgkillactive.py
5@@ -3,15 +3,12 @@
6 # Copyright 2009 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9-"""Kill transaction that have hung around for too long.
10-"""
11+"""Kill transactions that have hung around for too long."""
12
13 __all__ = []
14
15 import _pythonpath # noqa: F401
16
17-import os
18-import signal
19 import sys
20 from optparse import OptionParser
21
22@@ -34,7 +31,10 @@ def main():
23 type="int",
24 dest="max_seconds",
25 default=60 * 60,
26- help="Maximum seconds time connections are allowed to remain active.",
27+ help=(
28+ "Connections with a transaction older than MAX_SECONDS seconds "
29+ "will be killed. If 0, all matched connections will be killed."
30+ ),
31 )
32 parser.add_option(
33 "-q",
34@@ -76,7 +76,10 @@ def main():
35 """
36 SELECT usename, pid, backend_start, xact_start
37 FROM pg_stat_activity
38- WHERE xact_start < CURRENT_TIMESTAMP - '%d seconds'::interval %s
39+ WHERE
40+ pid <> pg_backend_pid()
41+ AND xact_start < CURRENT_TIMESTAMP - '%d seconds'::interval
42+ %s
43 ORDER BY pid
44 """
45 % (options.max_seconds, user_match_sql),
46@@ -88,7 +91,7 @@ def main():
47 if len(rows) == 0:
48 if not options.quiet:
49 print("No transactions to kill")
50- return 0
51+ return 0
52
53 for usename, pid, backend_start, transaction_start in rows:
54 print(
55@@ -101,7 +104,8 @@ def main():
56 )
57 )
58 if not options.dry_run:
59- os.kill(pid, signal.SIGTERM)
60+ cur.execute("SELECT pg_terminate_backend(%s)", (pid,))
61+ cur.close()
62 return 0
63
64
65diff --git a/utilities/pgkillidle.py b/utilities/pgkillidle.py
66index 9985ae8..6b6df3b 100755
67--- a/utilities/pgkillidle.py
68+++ b/utilities/pgkillidle.py
69@@ -3,15 +3,12 @@
70 # Copyright 2009 Canonical Ltd. This software is licensed under the
71 # GNU Affero General Public License version 3 (see the file LICENSE).
72
73-"""Kill <IDLE> in transaction connections that have hung around for too long.
74-"""
75+"""Kill idle-in-transaction connections that have hung around for too long."""
76
77 __all__ = []
78
79 import _pythonpath # noqa: F401
80
81-import os
82-import signal
83 import sys
84 from optparse import OptionParser
85
86@@ -64,17 +61,24 @@ def main():
87 if len(args) > 0:
88 parser.error("Too many arguments")
89
90- ignore_sql = " AND usename <> %s" * len(options.ignore or [])
91+ ignore_sql = " AND %s NOT IN (usename, application_name)" * len(
92+ options.ignore or []
93+ )
94
95 con = psycopg2.connect(options.connect_string)
96 cur = con.cursor()
97 cur.execute(
98 """
99- SELECT usename, pid, backend_start, query_start
100+ SELECT
101+ usename, application_name, datname, pid,
102+ backend_start, state_change, AGE(NOW(), state_change) AS age
103 FROM pg_stat_activity
104- WHERE query = '<IDLE> in transaction'
105- AND query_start < CURRENT_TIMESTAMP - '%d seconds'::interval %s
106- ORDER BY pid
107+ WHERE
108+ pid <> pg_backend_pid()
109+ AND state = 'idle in transaction'
110+ AND state_change < CURRENT_TIMESTAMP - '%d seconds'::interval
111+ %s
112+ ORDER BY age
113 """
114 % (options.max_idle_seconds, ignore_sql),
115 options.ignore,
116@@ -85,20 +89,17 @@ def main():
117 if len(rows) == 0:
118 if not options.quiet:
119 print("No IDLE transactions to kill")
120- return 0
121+ return 0
122
123- for usename, pid, backend_start, query_start in rows:
124- print(
125- "Killing %s(%d), %s, %s"
126- % (
127- usename,
128- pid,
129- backend_start,
130- query_start,
131- )
132- )
133+ for usename, appname, datname, pid, backend, state, age in rows:
134+ print(80 * "=")
135+ print("Killing %s(%d) %s from %s:" % (usename, pid, appname, datname))
136+ print(" backend start: %s" % (backend,))
137+ print(" idle start: %s" % (state,))
138+ print(" age: %s" % (age,))
139 if not options.dryrun:
140- os.kill(pid, signal.SIGTERM)
141+ cur.execute("SELECT pg_terminate_backend(%s)", (pid,))
142+ cur.close()
143 return 0
144
145

Subscribers

People subscribed via source and target branches

to status/vote changes: