~launchpad-pqm/launchpad/devel

10637.3.1 by Guilherme Salgado
Use the default python version instead of a hard-coded version
1
#!/usr/bin/python
8687.15.4 by Karl Fogel
Add the copyright header block to more files; tweak format in a few files.
2
#
3
# Copyright 2009 Canonical Ltd.  This software is licensed under the
4
# GNU Affero General Public License version 3 (see the file LICENSE).
5
1982 by Canonical.com Patch Queue Manager
[r=spiv] report some PostgreSQL stats we need to see
6
"""
7
Generate some statistics about a PostgreSQL database suitable for
8
emailing via cron
9
"""
10
11
__metaclass__ = type
12
13
import sys
14612.2.6 by William Grant
utilities
14
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
15
import psycopg2
1982 by Canonical.com Patch Queue Manager
[r=spiv] report some PostgreSQL stats we need to see
16
14612.2.6 by William Grant
utilities
17
1982 by Canonical.com Patch Queue Manager
[r=spiv] report some PostgreSQL stats we need to see
18
def percentage(num, total):
19
    """Return a percentage string of num/total"""
20
    if total == 0:
21
        return 'Unknown'
22
    else:
23
        return '%3.2f%%' % ( (num * 100.0) / total, )
24
25
26
def print_row(key, value):
27
    print '%(key)-20s: %(value)s' % vars()
28
29
1681.1.90 by Stuart Bishop
Sync pgstats.py with production
30
def pgstattuple(cur, table):
31
    """Return the result of PostgreSQL contribs's pgstattuple function
32
    """
33
    cur.execute("""
34
        SELECT
35
            table_len, tuple_count, tuple_len, tuple_percent,
36
            dead_tuple_count, dead_tuple_len, dead_tuple_percent,
37
            free_space, free_percent
38
        FROM pgstattuple(%(table)s)
39
        """, vars())
40
    pgstattuple = cur.fetchone()
41
    return {
42
        'name': table,
43
        'table_len': pgstattuple[0],
44
        'tuple_count': pgstattuple[1],
45
        'tuple_len': pgstattuple[2],
46
        'tuple_percent': pgstattuple[3],
47
        'dead_tuple_count': pgstattuple[4],
48
        'dead_tuple_len': pgstattuple[5],
49
        'dead_tuple_percent': pgstattuple[6],
50
        'free_space': pgstattuple[7],
51
        'free_percent': pgstattuple[8],
52
        }
53
54
1982 by Canonical.com Patch Queue Manager
[r=spiv] report some PostgreSQL stats we need to see
55
def main(dbname):
5821.2.85 by James Henstridge
Add "make check_launchpad_storm_on_merge" target that runs the tests
56
    con = psycopg2.connect("dbname=%s" % dbname)
1982 by Canonical.com Patch Queue Manager
[r=spiv] report some PostgreSQL stats we need to see
57
    cur = con.cursor()
58
59
    print 'Statistics for %s' % dbname
60
    print '===============' + '=' * (len(dbname))
61
62
    # Database level statistics
63
    cur.execute("""
64
        SELECT blks_hit, blks_read, numbackends,xact_commit, xact_rollback
65
            FROM pg_stat_database
66
            WHERE datname=%(dbname)s
67
        """, vars())
68
    hit, read, backends, commits, rollbacks = cur.fetchone()
69
70
    hit_rate = percentage(hit, hit + read)
71
72
    print_row("Cache hit rate", hit_rate)
73
    print_row("Number of backends", backends)
74
75
    commit_rate = percentage(commits, commits + rollbacks)
76
77
    print_row("Commit rate", commit_rate)
78
1681.1.90 by Stuart Bishop
Sync pgstats.py with production
79
    # Determine dead tuple bloat, if we have pgstattuple installed
80
    cur.execute("""
81
        SELECT COUNT(*) FROM pg_proc, pg_namespace
82
        WHERE pg_proc.pronamespace = pg_namespace.oid
83
            AND pg_namespace.nspname = 'public'
84
            AND proname = 'pgstattuple'
85
        """)
86
    pgstattuple_installed = (cur.fetchone()[0] > 0)
87
    if pgstattuple_installed:
88
        cur.execute("""
89
            SELECT nspname || '.' || relname
90
            FROM pg_class, pg_namespace
91
            WHERE pg_class.relnamespace = pg_namespace.oid
92
                AND pg_class.relkind = 'r'
93
                ORDER BY nspname, relname
94
            """)
95
        all_tables = [r[0] for r in cur.fetchall()]
96
        total_live_bytes = 0
97
        total_dead_bytes = 0
98
        stats = []
99
        for table in all_tables:
100
            stat = pgstattuple(cur, table)
101
            total_live_bytes += stat['tuple_len']
102
            total_dead_bytes += stat['dead_tuple_len']
103
            stats.append(stat)
104
        # Just report the worst offenders
105
        stats.sort(key=lambda x: x['dead_tuple_percent'], reverse=True)
106
        stats = [
107
            s for s in stats if s['dead_tuple_percent'] >= 10
108
                and s['dead_tuple_len'] >= 25 * 1024 * 1024
109
            ]
110
        def statstr(stat):
111
            name = stat['name']
112
            dead_tuple_percent = stat['dead_tuple_percent']
113
            dead_len = stat['dead_tuple_len'] / (1024*1024)
114
            return (
115
                    '%(name)s (%(dead_len)0.2fMB, '
116
                    '%(dead_tuple_percent)0.2f%%)' % vars()
117
                    )
118
        if len(stats) > 0:
119
            print_row('Needing vacuum', statstr(stats[0]))
120
            for stat in stats[1:]:
121
                print_row('', statstr(stat))
122
1982 by Canonical.com Patch Queue Manager
[r=spiv] report some PostgreSQL stats we need to see
123
    # Unused indexes, ignoring primary keys.
4664.1.1 by Curtis Hovey
Normalized comments for bug 3732.
124
    # XXX Stuart Bishop 2005-06-28:
125
    # We should identify constraints used to enforce uniqueness too
1982 by Canonical.com Patch Queue Manager
[r=spiv] report some PostgreSQL stats we need to see
126
    cur.execute("""
127
        SELECT relname, indexrelname
128
            FROM pg_stat_user_indexes AS u JOIN pg_indexes AS i
129
                ON u.schemaname = i.schemaname
130
                    AND u.relname = i.tablename
131
                    AND u.indexrelname = i.indexname
132
            WHERE
133
                idx_scan = 0
134
                AND indexrelname NOT LIKE '%_pkey'
135
                AND indexdef NOT LIKE 'CREATE UNIQUE %'
1681.1.127 by Stuart Bishop
Order unused index report
136
            ORDER BY relname, indexrelname
1982 by Canonical.com Patch Queue Manager
[r=spiv] report some PostgreSQL stats we need to see
137
        """)
138
139
    rows = cur.fetchall()
140
    if len(rows) == 0:
141
        print_row('Unused indexes', 'N/A')
142
    else:
143
        print_row('Unused indexes', rows[0][1])
144
        for table, index in rows[1:]:
145
            print_row('', index)
146
147
if __name__ == '__main__':
148
    if len(sys.argv) != 2:
149
        print >> sys.stderr, "Usage: %s [DBNAME]" % sys.argv[0]
150
        sys.exit(1)
151
    dbname = sys.argv[1]
152
    main(dbname)