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)
53
cursor = self.db.cursor()
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:
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
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,
67
def sync(self, feedback=None):
68
cursor = self.db.cursor()
70
cursor.execute("SELECT name, value FROM system WHERE name IN (%s)" %
71
','.join(["'%s'" % key for key in CACHE_METADATA_KEYS]))
73
for name, value in cursor:
74
metadata[name] = value
76
# -- check that we're populating the cache for the correct repository
77
repository_dir = metadata.get(CACHE_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.")
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,))
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))
93
self.db.commit() # save metadata changes made up to now
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')
99
self.youngest = metadata[CACHE_YOUNGEST_REV]
102
self.youngest = self.repos.normalize_rev(self.youngest)
103
if not self.youngest:
104
self.log.debug('normalize_rev failed (youngest_rev=%r)' %
107
self.log.debug('cache metadata undefined (youngest_rev=%r)' %
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)
69
if youngest_stored != str(self.repos.youngest_rev):
113
repos_youngest = self.repos.youngest_rev
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))
120
next_youngest = self.repos.next_rev(self.youngest)
124
next_youngest = self.repos.oldest_rev
125
next_youngest = self.repos.normalize_rev(next_youngest)
127
return # can't normalize oldest_rev: repository was empty
129
if next_youngest is None: # nothing to cache yet
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),))
136
# already there, but in progress, so keep ''previous''
137
# notion of 'youngest'
138
self.repos.clear(youngest_rev=self.youngest)
141
# 1. prepare for resyncing
142
# (there still might be a race condition at this point)
70
144
authz = self.repos.authz
71
145
self.repos.authz = Authorizer() # remove permission checking
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))
78
current_rev = self.repos.next_rev(youngest_stored)
81
current_rev = self.repos.oldest_rev
82
current_rev = self.repos.normalize_rev(current_rev)
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,
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)))
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)
104
self.repos.authz = authz # restore permission checking
151
while next_youngest is not None:
153
# 1.1 Attempt to resync the 'revision' table
154
self.log.info("Trying to sync revision [%s]" %
156
cset = self.repos.get_changeset(next_youngest)
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' %
166
# also potentially in progress, so keep ''previous''
167
# notion of 'youngest'
168
self.repos.clear(youngest_rev=self.youngest)
172
# 1.2. now *only* one process was able to get there
173
# (i.e. there *shouldn't* be any race condition here)
175
for path,kind,action,bpath,brev in cset.get_changes():
176
self.log.debug("Caching node change in [%s]: %s"
178
(path,kind,action,bpath,brev)))
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)",
186
path, kind, action, bpath, brev))
188
# 1.3. iterate (1.1 should always succeed now)
189
self.youngest = next_youngest
190
next_youngest = self.repos.next_rev(next_youngest)
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))
197
# 1.5. provide some feedback
199
feedback(self.youngest)
201
# 3. restore permission checking (after 1.)
202
self.repos.authz = authz
106
204
def get_node(self, path, rev=None):
107
205
return self.repos.get_node(path, rev)
109
def has_node(self, path, rev):
207
def has_node(self, path, rev=None):
110
208
return self.repos.has_node(path, rev)
112
210
def get_oldest_rev(self):
113
211
return self.repos.oldest_rev
115
213
def get_youngest_rev(self):
116
return self.repos.get_youngest_rev_in_cache(self.db)
214
if not hasattr(self, 'youngest'):
118
218
def previous_rev(self, rev):
119
219
return self.repos.previous_rev(rev)