~launchpad-pqm/launchpad/devel

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