10637.3.1
by Guilherme Salgado
Use the default python version instead of a hard-coded version |
1 |
#!/usr/bin/python
|
8452.3.3
by Karl Fogel
* utilities/: Add copyright header block to source files that were |
2 |
#
|
9893.10.4
by Stuart Bishop
Comment on mirrored copy of the script for production deployment |
3 |
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
|
8687.15.3
by Karl Fogel
Shorten the copyright header block to two lines. |
4 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
8452.3.3
by Karl Fogel
* utilities/: Add copyright header block to source files that were |
5 |
|
9893.10.4
by Stuart Bishop
Comment on mirrored copy of the script for production deployment |
6 |
# This file is mirrored into lp:losa-db-scripts, so please keep that
|
7 |
# version in sync with the master in the Launchpad tree.
|
|
8 |
||
1876
by Canonical.com Patch Queue Manager
[r=jamesh] database dump script |
9 |
"""
|
10 |
dropdb only more so.
|
|
11 |
||
9893.10.3
by Stuart Bishop
Ensure pgmassacre does not put the database cluster into recovery mode, and remove support for PostgreSQL versions earlier than 8.4 |
12 |
Cut off access, slaughter connections and burn the database to the ground
|
13 |
(but do nothing that could put the system into recovery mode).
|
|
1876
by Canonical.com Patch Queue Manager
[r=jamesh] database dump script |
14 |
"""
|
15 |
||
16 |
import sys |
|
17 |
import time |
|
5821.2.85
by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests |
18 |
import psycopg2 |
19 |
import psycopg2.extensions |
|
3432.1.2
by Stuart Bishop
pgmassacre should connect as default db user |
20 |
from optparse import OptionParser |
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
21 |
|
22 |
||
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
23 |
def connect(dbname='template1'): |
5282.4.3
by Stuart Bishop
Review feedback updates |
24 |
"""Connect to the database, returning the DB-API connection."""
|
3432.1.2
by Stuart Bishop
pgmassacre should connect as default db user |
25 |
if options.user is not None: |
5821.2.85
by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests |
26 |
return psycopg2.connect("dbname=%s user=%s" % (dbname, options.user)) |
3432.1.2
by Stuart Bishop
pgmassacre should connect as default db user |
27 |
else: |
5821.2.85
by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests |
28 |
return psycopg2.connect("dbname=%s" % dbname) |
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
29 |
|
30 |
||
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
31 |
def rollback_prepared_transactions(database): |
5282.4.3
by Stuart Bishop
Review feedback updates |
32 |
"""Rollback any prepared transactions.
|
33 |
||
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
34 |
PostgreSQL will refuse to drop a database with outstanding prepared
|
35 |
transactions.
|
|
5282.4.3
by Stuart Bishop
Review feedback updates |
36 |
"""
|
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
37 |
con = connect(database) |
9893.10.5
by Stuart Bishop
delint |
38 |
con.set_isolation_level(0) # Autocommit so we can ROLLBACK PREPARED. |
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
39 |
cur = con.cursor() |
40 |
||
41 |
# Get a list of outstanding prepared transactions.
|
|
42 |
cur.execute( |
|
43 |
"SELECT gid FROM pg_prepared_xacts WHERE database=%(database)s", |
|
5282.4.4
by Stuart Bishop
Update to standard 'icky parenthesis style |
44 |
vars()) |
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
45 |
xids = [row[0] for row in cur.fetchall()] |
46 |
for xid in xids: |
|
47 |
cur.execute("ROLLBACK PREPARED %(xid)s", vars()) |
|
48 |
con.close() |
|
49 |
||
50 |
||
9893.10.3
by Stuart Bishop
Ensure pgmassacre does not put the database cluster into recovery mode, and remove support for PostgreSQL versions earlier than 8.4 |
51 |
def still_open(database, max_wait=120): |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
52 |
"""Return True if there are still open connections, apart from our own.
|
53 |
||
9893.10.3
by Stuart Bishop
Ensure pgmassacre does not put the database cluster into recovery mode, and remove support for PostgreSQL versions earlier than 8.4 |
54 |
Waits a while to ensure that connections shutting down have a chance
|
55 |
to. This might take a while if there is a big transaction to
|
|
56 |
rollback.
|
|
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
57 |
"""
|
58 |
con = connect() |
|
9893.10.5
by Stuart Bishop
delint |
59 |
con.set_isolation_level(0) # Autocommit. |
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
60 |
cur = con.cursor() |
9893.10.3
by Stuart Bishop
Ensure pgmassacre does not put the database cluster into recovery mode, and remove support for PostgreSQL versions earlier than 8.4 |
61 |
# Keep checking until the timeout is reached, returning True if all
|
62 |
# of the backends are gone.
|
|
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
63 |
start = time.time() |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
64 |
while time.time() < start + max_wait: |
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
65 |
cur.execute(""" |
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
66 |
SELECT TRUE FROM pg_stat_activity
|
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
67 |
WHERE
|
68 |
datname=%(database)s |
|
69 |
AND procpid != pg_backend_pid()
|
|
70 |
LIMIT 1
|
|
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
71 |
""", vars()) |
72 |
if cur.fetchone() is None: |
|
73 |
return False |
|
9893.10.5
by Stuart Bishop
delint |
74 |
time.sleep(0.6) # Stats only updated every 500ms. |
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
75 |
con.close() |
76 |
return True |
|
77 |
||
5282.4.3
by Stuart Bishop
Review feedback updates |
78 |
|
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
79 |
def massacre(database): |
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
80 |
con = connect() |
9893.10.5
by Stuart Bishop
delint |
81 |
con.set_isolation_level(0) # Autocommit |
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
82 |
cur = con.cursor() |
83 |
||
5529.1.5
by Stuart Bishop
Make pgmassacre.py a little more bulletproof |
84 |
# Allow connections to the doomed database if something turned this off,
|
85 |
# such as an aborted run of this script.
|
|
86 |
cur.execute( |
|
87 |
"UPDATE pg_database SET datallowconn=TRUE WHERE datname=%s", |
|
88 |
[database]) |
|
89 |
||
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
90 |
# Rollback prepared transactions.
|
91 |
rollback_prepared_transactions(database) |
|
92 |
||
93 |
try: |
|
5282.4.3
by Stuart Bishop
Review feedback updates |
94 |
# Stop connections to the doomed database.
|
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
95 |
cur.execute( |
5529.1.5
by Stuart Bishop
Make pgmassacre.py a little more bulletproof |
96 |
"UPDATE pg_database SET datallowconn=FALSE WHERE datname=%s", |
5282.4.4
by Stuart Bishop
Update to standard 'icky parenthesis style |
97 |
[database]) |
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
98 |
|
9893.10.3
by Stuart Bishop
Ensure pgmassacre does not put the database cluster into recovery mode, and remove support for PostgreSQL versions earlier than 8.4 |
99 |
# New connections are disabled, but pg_stat_activity is only
|
100 |
# updated every 500ms. Ensure that pg_stat_activity has
|
|
101 |
# been refreshed to catch any connections that opened
|
|
102 |
# immediately before setting datallowconn.
|
|
103 |
time.sleep(1) |
|
104 |
||
105 |
# Terminate open connections.
|
|
106 |
cur.execute(""" |
|
107 |
SELECT procpid, pg_terminate_backend(procpid)
|
|
108 |
FROM pg_stat_activity
|
|
109 |
WHERE datname=%s AND procpid <> pg_backend_pid() |
|
110 |
""", [database]) |
|
9893.10.5
by Stuart Bishop
delint |
111 |
for procpid, success in cur.fetchall(): |
9893.10.3
by Stuart Bishop
Ensure pgmassacre does not put the database cluster into recovery mode, and remove support for PostgreSQL versions earlier than 8.4 |
112 |
if not success: |
113 |
print >> sys.stderr, ( |
|
114 |
"pg_terminate_backend(%s) failed" % procpid) |
|
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
115 |
con.close() |
116 |
||
117 |
if still_open(database): |
|
5282.4.3
by Stuart Bishop
Review feedback updates |
118 |
print >> sys.stderr, ( |
5282.4.4
by Stuart Bishop
Update to standard 'icky parenthesis style |
119 |
"Unable to kill all backends! Database not destroyed.") |
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
120 |
return 9 |
121 |
||
5282.4.3
by Stuart Bishop
Review feedback updates |
122 |
# Destroy the database.
|
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
123 |
con = connect() |
5282.4.3
by Stuart Bishop
Review feedback updates |
124 |
# AUTOCOMMIT required to execute commands like DROP DATABASE.
|
125 |
con.set_isolation_level(0) |
|
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
126 |
cur = con.cursor() |
9893.10.5
by Stuart Bishop
delint |
127 |
cur.execute("DROP DATABASE %s" % database) # Not quoted. |
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
128 |
con.close() |
129 |
return 0 |
|
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
130 |
finally: |
131 |
# In case something messed up, allow connections again so we can
|
|
132 |
# inspect the damage.
|
|
133 |
con = connect() |
|
134 |
con.set_isolation_level(0) |
|
135 |
cur = con.cursor() |
|
136 |
cur.execute( |
|
137 |
"UPDATE pg_database SET datallowconn=TRUE WHERE datname=%s", |
|
5282.4.4
by Stuart Bishop
Update to standard 'icky parenthesis style |
138 |
[database]) |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
139 |
con.close() |
140 |
||
141 |
||
142 |
def rebuild(database, template): |
|
143 |
if still_open(template, 20): |
|
144 |
print >> sys.stderr, ( |
|
145 |
"Giving up waiting for connections to %s to drop." % template) |
|
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
146 |
report_open_connections(template) |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
147 |
return 10 |
148 |
||
149 |
start = time.time() |
|
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
150 |
now = start |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
151 |
error_msg = None |
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
152 |
con = connect() |
9893.10.5
by Stuart Bishop
delint |
153 |
con.set_isolation_level(0) # Autocommit required for CREATE DATABASE. |
7675.357.8
by Stuart Bishop
Make database creation compatible with both PG 8.3 and PG 8.4 |
154 |
create_db_cmd = """ |
155 |
CREATE DATABASE %s WITH ENCODING='UTF8' TEMPLATE=%s |
|
156 |
""" % (database, template) |
|
157 |
# 8.4 allows us to create empty databases with a different locale
|
|
158 |
# to template1 by using the template0 database as a template.
|
|
159 |
# We make use of this feature so we don't have to care what locale
|
|
160 |
# was used to create the database cluster rather than requiring it
|
|
161 |
# to be rebuilt in the C locale.
|
|
9893.10.3
by Stuart Bishop
Ensure pgmassacre does not put the database cluster into recovery mode, and remove support for PostgreSQL versions earlier than 8.4 |
162 |
if template == "template0": |
7675.357.8
by Stuart Bishop
Make database creation compatible with both PG 8.3 and PG 8.4 |
163 |
create_db_cmd += "LC_COLLATE='C' LC_CTYPE='C'" |
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
164 |
while now < start + 20: |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
165 |
cur = con.cursor() |
166 |
try: |
|
7675.357.8
by Stuart Bishop
Make database creation compatible with both PG 8.3 and PG 8.4 |
167 |
cur.execute(create_db_cmd) |
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
168 |
con.close() |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
169 |
return 0 |
170 |
except psycopg2.Error, exception: |
|
171 |
error_msg = str(exception) |
|
9893.10.5
by Stuart Bishop
delint |
172 |
time.sleep(0.6) # Stats only updated every 500ms. |
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
173 |
now = time.time() |
174 |
con.close() |
|
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
175 |
|
176 |
print >> sys.stderr, "Unable to recreate database: %s" % error_msg |
|
177 |
return 11 |
|
178 |
||
179 |
||
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
180 |
def report_open_connections(database): |
181 |
con = connect() |
|
182 |
cur = con.cursor() |
|
183 |
cur.execute(""" |
|
184 |
SELECT usename, datname, count(*)
|
|
185 |
FROM pg_stat_activity
|
|
186 |
WHERE procpid != pg_backend_pid()
|
|
187 |
GROUP BY usename, datname
|
|
188 |
ORDER BY datname, usename
|
|
189 |
""", [database]) |
|
190 |
for usename, datname, num_connections in cur.fetchall(): |
|
191 |
print >> sys.stderr, "%d connections by %s to %s" % ( |
|
192 |
num_connections, usename, datname) |
|
193 |
con.close() |
|
194 |
||
195 |
||
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
196 |
options = None |
197 |
||
9893.10.5
by Stuart Bishop
delint |
198 |
|
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
199 |
def main(): |
8137.13.2
by Stuart Bishop
Improve usage string |
200 |
parser = OptionParser("Usage: %prog [options] DBNAME") |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
201 |
parser.add_option("-U", "--user", dest="user", default=None, |
202 |
help="Connect as USER", metavar="USER") |
|
203 |
parser.add_option("-t", "--template", dest="template", default=None, |
|
9893.10.3
by Stuart Bishop
Ensure pgmassacre does not put the database cluster into recovery mode, and remove support for PostgreSQL versions earlier than 8.4 |
204 |
help="Recreate database using DBNAME as a template database." |
205 |
" If template0, database will be created in the C locale.", |
|
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
206 |
metavar="DBNAME") |
207 |
global options |
|
208 |
(options, args) = parser.parse_args() |
|
209 |
||
210 |
if len(args) != 1: |
|
211 |
parser.error('Must specify one, and only one, database to destroy') |
|
212 |
||
213 |
database = args[0] |
|
214 |
||
215 |
# Don't be stupid protection.
|
|
216 |
if database in ('template1', 'template0'): |
|
8137.13.5
by Stuart Bishop
Tweak error message |
217 |
parser.error( |
218 |
"Running this script against template1 or template0 is nuts.") |
|
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
219 |
|
220 |
con = connect() |
|
221 |
cur = con.cursor() |
|
7675.357.8
by Stuart Bishop
Make database creation compatible with both PG 8.3 and PG 8.4 |
222 |
|
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
223 |
# Ensure the template database exists.
|
224 |
if options.template is not None: |
|
225 |
cur.execute( |
|
226 |
"SELECT TRUE FROM pg_database WHERE datname=%s", |
|
227 |
[options.template]) |
|
228 |
if cur.fetchone() is None: |
|
229 |
parser.error( |
|
230 |
"Template database %s does not exist." % options.template) |
|
231 |
# If the database doesn't exist, no point attempting to drop it.
|
|
232 |
cur.execute("SELECT TRUE FROM pg_database WHERE datname=%s", [database]) |
|
233 |
db_exists = cur.fetchone() is not None |
|
234 |
con.close() |
|
235 |
||
236 |
if db_exists: |
|
237 |
rv = massacre(database) |
|
238 |
if rv != 0: |
|
8137.13.3
by Stuart Bishop
Add some instrumentation to pgmassacre.py creation |
239 |
print >> sys.stderr, "Fail %d" % rv |
8137.13.1
by Stuart Bishop
Option for pgmassacre.py to rebuild the database it just destroyed from a template |
240 |
return rv |
241 |
||
242 |
if options.template is not None: |
|
243 |
return rebuild(database, options.template) |
|
244 |
else: |
|
245 |
return 0 |
|
5282.4.2
by Stuart Bishop
Make pgpassacre.py rollback outstanding prepared transactions to avoid 'still being accessed by other users' error. |
246 |
|
3432.1.1
by Stuart Bishop
Update pgmassacre to work as unix user other than 'postgres' |
247 |
|
248 |
if __name__ == '__main__': |
|
249 |
sys.exit(main()) |