~launchpad-pqm/launchpad/devel

2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
1
#!/usr/bin/env python
3149.1.5 by Stuart Bishop
Staging config
2
# Copyright 2005-2006 Canonical Ltd.  All rights reserved.
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
3
2125 by Canonical.com Patch Queue Manager
[r=bjornt] Cronscript refactorings
4
import _pythonpath
5
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
6
import sys
2125 by Canonical.com Patch Queue Manager
[r=bjornt] Cronscript refactorings
7
from optparse import OptionParser
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
8
9
from zope.component import getUtility
10
11
from canonical.config import config
2976.4.1 by Stuart Bishop
Make karma update cronscript a lot less database intensive.
12
from canonical.lp import initZopeless, AUTOCOMMIT_ISOLATION
2125 by Canonical.com Patch Queue Manager
[r=bjornt] Cronscript refactorings
13
from canonical.launchpad.scripts import (
14
        execute_zcml_for_scripts, logger_options, logger
15
        )
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
16
from canonical.launchpad.scripts.lockfile import LockFile
17
from canonical.launchpad.interfaces import IPersonSet
3149.1.4 by Stuart Bishop
Make foaf-update-karma-cache friendlier and less likely to trigger deadlocks
18
from canonical.database.sqlbase import connect
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
19
20
_default_lock_file = '/var/lock/launchpad-karma-update.lock'
21
22
23
def update_karma_cache():
24
    """Update the KarmaCache table for all valid Launchpad users.
25
26
    For each Launchpad user with a preferred email address, calculate his
27
    karmavalue for each category of actions we have and update his entry in
28
    the KarmaCache table. If a user doesn't have an entry for that category in
29
    KarmaCache a new one will be created.
30
    """
2976.4.4 by Stuart Bishop
Updates from review
31
    # We use the autocommit transaction isolation level to minimize
32
    # contention, and also allows us to not bother explicitly calling
33
    # COMMIT all the time. This script in no way relies on transactions,
34
    # so it is safe.
2976.4.1 by Stuart Bishop
Make karma update cronscript a lot less database intensive.
35
    ztm = initZopeless(
36
            dbuser=config.karmacacheupdater.dbuser,
37
            implicitBegin=False, isolation=AUTOCOMMIT_ISOLATION
38
            )
3149.1.4 by Stuart Bishop
Make foaf-update-karma-cache friendlier and less likely to trigger deadlocks
39
    con = connect(config.karmacacheupdater.dbuser)
40
    con.set_isolation_level(AUTOCOMMIT_ISOLATION)
41
    cur = con.cursor()
2976.4.1 by Stuart Bishop
Make karma update cronscript a lot less database intensive.
42
    karma_expires_after = '1 year'
3149.1.4 by Stuart Bishop
Make foaf-update-karma-cache friendlier and less likely to trigger deadlocks
43
2976.4.1 by Stuart Bishop
Make karma update cronscript a lot less database intensive.
44
    # Calculate everyones karma. Karma degrades each day, becoming
45
    # worthless after karma_expires_after. This query produces odd results
46
    # when datecreated is in the future, but there is really no point adding
47
    # the extra WHEN clause.
2976.4.2 by Stuart Bishop
Drop person.karma, replacing with new KarmaTotalCache table
48
    log.info("Calculating everyones karma")
2976.4.1 by Stuart Bishop
Make karma update cronscript a lot less database intensive.
49
    cur.execute("""
50
        SELECT person, category, ROUND(SUM(
51
            CASE WHEN datecreated + %(karma_expires_after)s::interval
52
                <= CURRENT_TIMESTAMP AT TIME ZONE 'UTC' THEN 0
53
            ELSE points * (1 - extract(
54
                EPOCH FROM CURRENT_TIMESTAMP AT TIME ZONE 'UTC' - datecreated
55
                ) / extract(EPOCH FROM %(karma_expires_after)s::interval))
56
            END
57
            ))
58
        FROM Karma, KarmaAction
59
        WHERE action = KarmaAction.id
60
        GROUP BY person, category
61
        """, vars())
62
63
    # Suck into RAM to avoid tieing up resources on the DB.
64
    results = list(cur.fetchall())
65
2976.4.2 by Stuart Bishop
Drop person.karma, replacing with new KarmaTotalCache table
66
    log.info("Got %d (person, category) scores", len(results))
67
2976.4.1 by Stuart Bishop
Make karma update cronscript a lot less database intensive.
68
    # Note that we don't need to commit each iteration because we are running
69
    # in autocommit mode.
70
    for person, category, points in results:
71
        log.debug(
72
            "Setting person=%(person)d, category=%(category)d, "
73
            "points=%(points)d", vars()
74
            )
3149.1.4 by Stuart Bishop
Make foaf-update-karma-cache friendlier and less likely to trigger deadlocks
75
76
        if points <= 0:
77
            # Don't allow our table to bloat with inactive users
78
            cur.execute("""
79
                DELETE FROM KarmaCache WHERE
80
                person=%(person)s AND category=%(category)s
81
                """, vars())
82
        else:
83
            # Attempt to UPDATE. If no rows modified, perform an INSERT
84
            cur.execute("""
85
                UPDATE KarmaCache SET karmavalue=%(points)s
86
                WHERE person=%(person)s AND category=%(category)s
87
                """, vars())
88
            assert cur.rowcount in (0, 1), \
89
                    'Bad rowcount %r returned from DML' % (cur.rowcount,)
90
            if cur.rowcount == 0:
91
                cur.execute("""
92
                    INSERT INTO KarmaCache (person, category, karmavalue)
93
                    VALUES (%(person)s, %(category)s, %(points)s)
94
                    """, vars())
95
96
    # VACUUM KarmaCache since we have just touched every record in it
97
    cur.execute("""VACUUM KarmaCache""")
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
98
2976.4.2 by Stuart Bishop
Drop person.karma, replacing with new KarmaTotalCache table
99
    # Update the KarmaTotalCache table
100
    log.info("Rebuilding KarmaTotalCache")
3149.1.4 by Stuart Bishop
Make foaf-update-karma-cache friendlier and less likely to trigger deadlocks
101
    # Trash old records
102
    cur.execute("""
103
        DELETE FROM KarmaTotalCache
104
        WHERE person NOT IN (SELECT person FROM KarmaCache)
105
        """)
106
    # Update existing records
107
    cur.execute("""
108
        UPDATE KarmaTotalCache SET karma_total=sum_karmavalue
109
        FROM (
110
            SELECT person AS sum_person, SUM(karmavalue) AS sum_karmavalue
111
            FROM KarmaCache GROUP BY person
112
            ) AS sums
113
        WHERE KarmaTotalCache.person = sum_person
114
        """)
115
116
    # VACUUM KarmaTotalCache since we have just touched every row in it.
117
    cur.execute("""VACUUM KarmaTotalCache""")
118
119
    # Insert new records into the KarmaTotalCache table. If deadlocks
120
    # become a problem, first LOCK the corresponding rows in the Person table
121
    # so the bulk insert cannot fail. We don't bother at the moment as this
122
    # would involve granting UPDATE rights on the Person table to the
123
    # karmacacheupdater user.
124
    ## cur.execute("BEGIN")
125
    ## cur.execute("""
126
    ##     SELECT * FROM Person
127
    ##     WHERE id NOT IN (SELECT person FROM KarmaTotalCache)
128
    ##     FOR UPDATE
129
    ##     """)
2976.4.2 by Stuart Bishop
Drop person.karma, replacing with new KarmaTotalCache table
130
    cur.execute("""
131
        INSERT INTO KarmaTotalCache (person, karma_total)
132
        SELECT person, SUM(karmavalue) FROM KarmaCache
3149.1.4 by Stuart Bishop
Make foaf-update-karma-cache friendlier and less likely to trigger deadlocks
133
        WHERE person NOT IN (SELECT person FROM KarmaTotalCache)
2976.4.2 by Stuart Bishop
Drop person.karma, replacing with new KarmaTotalCache table
134
        GROUP BY person
135
        """)
3149.1.4 by Stuart Bishop
Make foaf-update-karma-cache friendlier and less likely to trigger deadlocks
136
    ## cur.execute("COMMIT")
137
2976.4.2 by Stuart Bishop
Drop person.karma, replacing with new KarmaTotalCache table
138
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
139
if __name__ == '__main__':
2125 by Canonical.com Patch Queue Manager
[r=bjornt] Cronscript refactorings
140
    parser = OptionParser()
141
    logger_options(parser)
142
    (options, arguments) = parser.parse_args()
143
    if arguments:
144
        parser.error("Unhandled arguments %s" % repr(arguments))
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
145
    execute_zcml_for_scripts()
146
2125 by Canonical.com Patch Queue Manager
[r=bjornt] Cronscript refactorings
147
    log = logger(options, 'karmacache')
148
    log.info("Updating the karma cache of Launchpad users.")
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
149
2125 by Canonical.com Patch Queue Manager
[r=bjornt] Cronscript refactorings
150
    lockfile = LockFile(_default_lock_file, logger=log)
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
151
    try:
152
        lockfile.acquire()
153
    except OSError:
2125 by Canonical.com Patch Queue Manager
[r=bjornt] Cronscript refactorings
154
        log.info("lockfile %s already exists, exiting", _default_lock_file)
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
155
        sys.exit(1)
156
157
    try:
158
        update_karma_cache()
159
    finally:
160
        lockfile.release()
161
2125 by Canonical.com Patch Queue Manager
[r=bjornt] Cronscript refactorings
162
    log.info("Finished updating the karma cache of Launchpad users.")
2002 by Canonical.com Patch Queue Manager
Implements the karma framework (according to the KarmaImplementation spec) and hook it into Malone events. r=spiv,stub
163