~ubuntu-branches/ubuntu/precise/trac/precise

« back to all changes in this revision

Viewing changes to trac/versioncontrol/cache.py

  • Committer: Bazaar Package Importer
  • Author(s): Luis Matos
  • Date: 2007-05-20 22:46:56 UTC
  • mfrom: (1.1.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20070520224656-3g5unjo0c4glevjh
Tags: 0.10.4-1
* New upstream release (Closes: #414134, #420219)
* Fixed typo in debian/copyright file (Closes: #422409)

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
              'D': Changeset.DELETE, 'E': Changeset.EDIT,
25
25
              'M': Changeset.MOVE}
26
26
 
 
27
CACHE_REPOSITORY_DIR = 'repository_dir'
 
28
CACHE_YOUNGEST_REV = 'youngest_rev'
 
29
 
 
30
CACHE_METADATA_KEYS = (CACHE_REPOSITORY_DIR, CACHE_YOUNGEST_REV)
 
31
 
27
32
 
28
33
class CachedRepository(Repository):
29
34
 
31
36
        Repository.__init__(self, repos.name, authz, log)
32
37
        self.db = db
33
38
        self.repos = repos
34
 
        self.sync()
35
39
 
36
40
    def close(self):
37
41
        self.repos.close()
46
50
                       "WHERE time >= %s AND time < %s "
47
51
                       "ORDER BY time", (start, stop))
48
52
        for rev, in cursor:
49
 
            if self.authz.has_permission_for_changeset(rev):
50
 
                yield self.get_changeset(rev)
51
 
 
52
 
    def sync(self):
53
 
        cursor = self.db.cursor()
54
 
 
55
 
        # -- repository used for populating the cache
56
 
        cursor.execute("SELECT value FROM system WHERE name='repository_dir'")
57
 
        for previous_repository_dir, in cursor:
58
 
            if previous_repository_dir != self.name:
 
53
            try:
 
54
                if self.authz.has_permission_for_changeset(rev):
 
55
                    yield self.get_changeset(rev)
 
56
            except NoSuchChangeset:
 
57
                pass # skip changesets currently being resync'ed
 
58
 
 
59
    def sync_changeset(self, rev):
 
60
        cset = self.repos.get_changeset(rev)
 
61
        cursor = self.db.cursor()
 
62
        cursor.execute("UPDATE revision SET time=%s, author=%s, message=%s "
 
63
                       "WHERE rev=%s", (cset.date, cset.author, cset.message,
 
64
                                        (str(cset.rev))))
 
65
        self.db.commit()
 
66
        
 
67
    def sync(self, feedback=None):
 
68
        cursor = self.db.cursor()
 
69
 
 
70
        cursor.execute("SELECT name, value FROM system WHERE name IN (%s)" %
 
71
                       ','.join(["'%s'" % key for key in CACHE_METADATA_KEYS]))
 
72
        metadata = {}
 
73
        for name, value in cursor:
 
74
            metadata[name] = value
 
75
        
 
76
        # -- check that we're populating the cache for the correct repository
 
77
        repository_dir = metadata.get(CACHE_REPOSITORY_DIR)
 
78
        if repository_dir:
 
79
            if repository_dir != self.name:
 
80
                self.log.info("'repository_dir' has changed from %r to %r"
 
81
                              % (repository_dir, self.name))
59
82
                raise TracError("The 'repository_dir' has changed, "
60
83
                                "a 'trac-admin resync' operation is needed.")
61
 
            break
62
 
        else: # no 'repository_dir' stored yet, assume everything's OK
63
 
            cursor.execute("INSERT INTO system (name,value) "
64
 
                           "VALUES ('repository_dir',%s)", (self.name,))
65
 
 
 
84
        elif repository_dir is None: # 
 
85
            self.log.info('Storing initial "repository_dir": %s' % self.name)
 
86
            cursor.execute("INSERT INTO system (name,value) VALUES (%s,%s)",
 
87
                           (CACHE_REPOSITORY_DIR, self.name,))
 
88
        else: # 'repository_dir' cleared by a resync
 
89
            self.log.info('Resetting "repository_dir": %s' % self.name)
 
90
            cursor.execute("UPDATE system SET value=%s WHERE name=%s",
 
91
                           (self.name, CACHE_REPOSITORY_DIR))
 
92
 
 
93
        self.db.commit() # save metadata changes made up to now
 
94
 
 
95
        # -- retrieve the youngest revision cached so far
 
96
        if CACHE_YOUNGEST_REV not in metadata:
 
97
            raise TracError('Missing "youngest_rev" in cache metadata')
 
98
        
 
99
        self.youngest = metadata[CACHE_YOUNGEST_REV]
 
100
 
 
101
        if self.youngest:
 
102
            self.youngest = self.repos.normalize_rev(self.youngest)
 
103
            if not self.youngest:
 
104
                self.log.debug('normalize_rev failed (youngest_rev=%r)' %
 
105
                               self.youngest_rev)
 
106
        else:
 
107
            self.log.debug('cache metadata undefined (youngest_rev=%r)' %
 
108
                           self.youngest_rev)
 
109
            self.youngest = None
 
110
 
 
111
        # -- retrieve the youngest revision in the repository
66
112
        self.repos.clear()
67
 
        youngest_stored = self.repos.get_youngest_rev_in_cache(self.db)
68
 
 
69
 
        if youngest_stored != str(self.repos.youngest_rev):
 
113
        repos_youngest = self.repos.youngest_rev
 
114
 
 
115
        # -- compare them and try to resync if different
 
116
        if self.youngest != repos_youngest:
 
117
            self.log.info("repos rev [%s] != cached rev [%s]" %
 
118
                          (repos_youngest, self.youngest))
 
119
            if self.youngest:
 
120
                next_youngest = self.repos.next_rev(self.youngest)
 
121
            else:
 
122
                next_youngest = None
 
123
                try:
 
124
                    next_youngest = self.repos.oldest_rev
 
125
                    next_youngest = self.repos.normalize_rev(next_youngest)
 
126
                except TracError:
 
127
                    return # can't normalize oldest_rev: repository was empty
 
128
 
 
129
            if next_youngest is None: # nothing to cache yet
 
130
                return
 
131
 
 
132
            # 0. first check if there's no (obvious) resync in progress
 
133
            cursor.execute("SELECT rev FROM revision WHERE rev=%s",
 
134
                           (str(next_youngest),))
 
135
            for rev, in cursor:
 
136
                # already there, but in progress, so keep ''previous''
 
137
                # notion of 'youngest'
 
138
                self.repos.clear(youngest_rev=self.youngest)
 
139
                return
 
140
 
 
141
            # 1. prepare for resyncing
 
142
            #    (there still might be a race condition at this point)
 
143
 
70
144
            authz = self.repos.authz
71
145
            self.repos.authz = Authorizer() # remove permission checking
72
146
 
73
147
            kindmap = dict(zip(_kindmap.values(), _kindmap.keys()))
74
148
            actionmap = dict(zip(_actionmap.values(), _actionmap.keys()))
75
 
            self.log.info("Syncing with repository (%s to %s)"
76
 
                          % (youngest_stored, self.repos.youngest_rev))
77
 
            if youngest_stored:
78
 
                current_rev = self.repos.next_rev(youngest_stored)
79
 
            else:
80
 
                try:
81
 
                    current_rev = self.repos.oldest_rev
82
 
                    current_rev = self.repos.normalize_rev(current_rev)
83
 
                except TracError:
84
 
                    current_rev = None
85
 
            while current_rev is not None:
86
 
                changeset = self.repos.get_changeset(current_rev)
87
 
                cursor.execute("INSERT INTO revision (rev,time,author,message) "
88
 
                               "VALUES (%s,%s,%s,%s)", (str(current_rev),
89
 
                               changeset.date, changeset.author,
90
 
                               changeset.message))
91
 
                for path,kind,action,base_path,base_rev in changeset.get_changes():
92
 
                    self.log.debug("Caching node change in [%s]: %s"
93
 
                                   % (current_rev, (path, kind, action,
94
 
                                      base_path, base_rev)))
95
 
                    kind = kindmap[kind]
96
 
                    action = actionmap[action]
97
 
                    cursor.execute("INSERT INTO node_change (rev,path,"
98
 
                                   "node_type,change_type,base_path,base_rev) "
99
 
                                   "VALUES (%s,%s,%s,%s,%s,%s)",
100
 
                                   (str(current_rev), path, kind, action,
101
 
                                   base_path, base_rev))
102
 
                current_rev = self.repos.next_rev(current_rev)
103
 
            self.db.commit()
104
 
            self.repos.authz = authz # restore permission checking
 
149
 
 
150
            try:
 
151
                while next_youngest is not None:
 
152
                    
 
153
                    # 1.1 Attempt to resync the 'revision' table
 
154
                    self.log.info("Trying to sync revision [%s]" %
 
155
                                  next_youngest)
 
156
                    cset = self.repos.get_changeset(next_youngest)
 
157
                    try:
 
158
                        cursor.execute("INSERT INTO revision "
 
159
                                       " (rev,time,author,message) "
 
160
                                       "VALUES (%s,%s,%s,%s)",
 
161
                                       (str(next_youngest), cset.date,
 
162
                                        cset.author, cset.message))
 
163
                    except Exception, e: # *another* 1.1. resync attempt won 
 
164
                        self.log.warning('Revision %s already cached: %s' %
 
165
                                         (next_youngest, e))
 
166
                        # also potentially in progress, so keep ''previous''
 
167
                        # notion of 'youngest'
 
168
                        self.repos.clear(youngest_rev=self.youngest)
 
169
                        self.db.rollback()
 
170
                        return
 
171
 
 
172
                    # 1.2. now *only* one process was able to get there
 
173
                    #      (i.e. there *shouldn't* be any race condition here)
 
174
 
 
175
                    for path,kind,action,bpath,brev in cset.get_changes():
 
176
                        self.log.debug("Caching node change in [%s]: %s"
 
177
                                       % (next_youngest,
 
178
                                          (path,kind,action,bpath,brev)))
 
179
                        kind = kindmap[kind]
 
180
                        action = actionmap[action]
 
181
                        cursor.execute("INSERT INTO node_change "
 
182
                                       " (rev,path,node_type,change_type, "
 
183
                                       "  base_path,base_rev) "
 
184
                                       "VALUES (%s,%s,%s,%s,%s,%s)",
 
185
                                       (str(next_youngest),
 
186
                                        path, kind, action, bpath, brev))
 
187
 
 
188
                    # 1.3. iterate (1.1 should always succeed now)
 
189
                    self.youngest = next_youngest                    
 
190
                    next_youngest = self.repos.next_rev(next_youngest)
 
191
 
 
192
                    # 1.4. update 'youngest_rev' metadata (minimize failures at 0.)
 
193
                    cursor.execute("UPDATE system SET value=%s WHERE name=%s",
 
194
                                   (str(self.youngest), CACHE_YOUNGEST_REV))
 
195
                    self.db.commit()
 
196
 
 
197
                    # 1.5. provide some feedback
 
198
                    if feedback:
 
199
                        feedback(self.youngest)
 
200
            finally:
 
201
                # 3. restore permission checking (after 1.)
 
202
                self.repos.authz = authz
105
203
 
106
204
    def get_node(self, path, rev=None):
107
205
        return self.repos.get_node(path, rev)
108
206
 
109
 
    def has_node(self, path, rev):
 
207
    def has_node(self, path, rev=None):
110
208
        return self.repos.has_node(path, rev)
111
209
 
112
210
    def get_oldest_rev(self):
113
211
        return self.repos.oldest_rev
114
212
 
115
213
    def get_youngest_rev(self):
116
 
        return self.repos.get_youngest_rev_in_cache(self.db)
 
214
        if not hasattr(self, 'youngest'):
 
215
            self.sync()
 
216
        return self.youngest
117
217
 
118
218
    def previous_rev(self, rev):
119
219
        return self.repos.previous_rev(rev)