Ticket #4586: sync_using_youngest_rev_metadata-r4871.3.diff
| File sync_using_youngest_rev_metadata-r4871.3.diff, 14.0 KB (added by cboos, 21 months ago) |
|---|
-
trac/versioncontrol/api.py
137 137 """Close the connection to the repository.""" 138 138 raise NotImplementedError 139 139 140 def clear(self): 141 """Clear any data that may have been cached in instance properties.""" 140 def clear(self, youngest_rev=None): 141 """Clear any data that may have been cached in instance properties. 142 143 `youngest_rev` can be specified as a way to force the value 144 of the `youngest_rev` property (''will change in 0.12''). 145 """ 142 146 pass 143 147 144 148 def get_quickjump_entries(self, rev): … … 222 226 The way revisions are sequenced is version control specific. 223 227 By default, one assumes that the revisions are sequenced in time 224 228 (... which is ''not'' correct for most VCS, including Subversion). 229 230 (Deprecated, will not be used anymore in Trac 0.12) 225 231 """ 226 232 cursor = db.cursor() 227 233 cursor.execute("SELECT rev FROM revision ORDER BY time DESC LIMIT 1") -
trac/versioncontrol/tests/cache.py
31 31 def setUp(self): 32 32 self.db = InMemoryDatabase() 33 33 self.log = logger_factory('test') 34 cursor = self.db.cursor() 35 cursor.execute("INSERT INTO system (name, value) VALUES (%s,%s)", 36 ('youngest_rev', '')) 34 37 35 38 def test_initial_sync_with_empty_repos(self): 36 39 t = datetime(2001, 1, 1, 1, 1, 1, 0, utc) … … 96 99 "VALUES ('1',%s,%s,%s,%s,%s)", 97 100 [('trunk', 'D', 'A', None, None), 98 101 ('trunk/README', 'F', 'A', None, None)]) 102 cursor.execute("UPDATE system SET value='1' WHERE name='youngest_rev'") 99 103 100 104 changes = [('trunk/README', Node.FILE, Changeset.EDIT, 'trunk/README', 1)] 101 105 changeset = Mock(Changeset, 2, 'Update', 'joe', t3, … … 103 107 repos = Mock(Repository, 'test-repos', None, self.log, 104 108 get_changeset=lambda x: changeset, 105 109 get_youngest_rev=lambda: 2, 106 next_rev=lambda x: int(x) == 1 and 2 or None) 110 get_oldest_rev=lambda: 0, 111 normalize_rev=lambda x: x, 112 next_rev=lambda x: x and int(x) == 1 and 2 or None) 107 113 cache = CachedRepository(self.db, repos, None, self.log) 108 114 cache.sync() 109 115 … … 130 136 "VALUES ('1',%s,%s,%s,%s,%s)", 131 137 [('trunk', 'D', 'A', None, None), 132 138 ('trunk/README', 'F', 'A', None, None)]) 139 cursor.execute("UPDATE system SET value='1' WHERE name='youngest_rev'") 133 140 134 141 repos = Mock(Repository, 'test-repos', None, self.log, 135 142 get_changeset=lambda x: None, 136 143 get_youngest_rev=lambda: 1, 137 next_rev=lambda x: None, normalize_rev=lambda rev: rev) 144 get_oldest_rev=lambda: 0, 145 next_rev=lambda x: None, 146 normalize_rev=lambda rev: rev) 138 147 cache = CachedRepository(self.db, repos, None, self.log) 139 148 self.assertEqual('1', cache.youngest_rev) 140 149 changeset = cache.get_changeset(1) -
trac/versioncontrol/svn_fs.py
392 392 assert self.scope[0] == '/' 393 393 self.clear() 394 394 395 def clear(self ):395 def clear(self, youngest_rev=None): 396 396 self.youngest = None 397 if youngest_rev is not None: 398 self.youngest = self.normalize_rev(youngest_rev) 397 399 self.oldest = None 398 400 399 401 def __del__(self): -
trac/versioncontrol/cache.py
27 27 'D': Changeset.DELETE, 'E': Changeset.EDIT, 28 28 'M': Changeset.MOVE} 29 29 30 CACHE_REPOSITORY_DIR = 'repository_dir' 31 CACHE_YOUNGEST_REV = 'youngest_rev' 30 32 31 33 class CachedRepository(Repository): 32 34 … … 34 36 Repository.__init__(self, repos.name, authz, log) 35 37 self.db = db 36 38 self.repos = repos 37 try: 38 self.sync() 39 except TracError: 40 raise 41 except Exception, e: # most probably 2 concurrent resync attempts 42 log.warning('Error during sync(): %s' % e) 39 self.sync() 43 40 44 41 def close(self): 45 42 self.repos.close() … … 64 61 def sync(self): 65 62 cursor = self.db.cursor() 66 63 67 # -- repository used for populating the cache 68 cursor.execute("SELECT value FROM system WHERE name='repository_dir'") 69 for previous_repository_dir, in cursor: 70 if previous_repository_dir != self.name: 64 cursor.execute("SELECT name, value FROM system " 65 "WHERE name IN ('%s', '%s')" % 66 (CACHE_REPOSITORY_DIR, CACHE_YOUNGEST_REV)) 67 metadata = {} 68 for name, value in cursor: 69 metadata[name] = value 70 71 # -- check that we're populating the cache for the correct repository 72 repository_dir = metadata.get(CACHE_REPOSITORY_DIR) 73 if repository_dir: 74 if repository_dir != self.name: 71 75 raise TracError("The 'repository_dir' has changed, " 72 76 "a 'trac-admin resync' operation is needed.") 73 break74 77 else: # no 'repository_dir' stored yet, assume everything's OK 75 cursor.execute("INSERT INTO system (name,value) "76 "VALUES ('repository_dir',%s)", (self.name,))78 cursor.execute("INSERT INTO system (name,value) VALUES (%s,%s)", 79 (CACHE_REPOSITORY_DIR, self.name,)) 77 80 81 # -- retrieve the youngest revision cached so far 82 if CACHE_YOUNGEST_REV not in metadata: 83 # ''upgrade'' using the legacy `get_youngest_rev_in_cache` method 84 self.youngest = self.repos.get_youngest_rev_in_cache(self.db) or '' 85 cursor.execute("INSERT INTO system (name, value) VALUES (%s, %s)", 86 (CACHE_YOUNGEST_REV, self.youngest)) 87 self.log.info('Upgraded cache metadata (youngest_rev=%s)' % 88 self.youngest_rev) 89 else: 90 self.youngest = metadata[CACHE_YOUNGEST_REV] 91 92 if self.youngest: 93 self.youngest = self.repos.normalize_rev(self.youngest) 94 else: 95 self.youngest = None 96 97 # -- retrieve the youngest revision in the repository 78 98 self.repos.clear() 79 youngest_stored = self.repos.get_youngest_rev_in_cache(self.db)99 repos_youngest = self.repos.youngest_rev 80 100 81 if youngest_stored != str(self.repos.youngest_rev): 101 # -- compare them and try to resync if different 102 if self.youngest != repos_youngest: 103 if self.youngest: 104 next_youngest = self.repos.next_rev(self.youngest) 105 else: 106 next_youngest = None 107 try: 108 next_youngest = self.repos.oldest_rev 109 next_youngest = self.repos.normalize_rev(next_youngest) 110 except TracError: 111 pass 112 113 if next_youngest is None: # nothing to cache yet 114 return 115 116 # 0. first check if there's no (obvious) resync in progress 117 cursor.execute("SELECT rev FROM revision WHERE rev=%s", 118 (str(next_youngest),)) 119 for rev, in cursor: 120 # already there, but in progress, so keep ''previous'' 121 # notion of 'youngest' 122 self.repos.clear(youngest_rev=self.youngest) 123 return 124 125 # 1. prepare for resyncing 126 # (there still might be a race condition at this point) 127 82 128 authz = self.repos.authz 83 129 self.repos.authz = Authorizer() # remove permission checking 84 130 85 131 kindmap = dict(zip(_kindmap.values(), _kindmap.keys())) 86 132 actionmap = dict(zip(_actionmap.values(), _actionmap.keys())) 87 self.log.info("Syncing with repository (%s to %s)"88 % (youngest_stored, self.repos.youngest_rev))89 if youngest_stored:90 current_rev = self.repos.next_rev(youngest_stored)91 else:92 try:93 current_rev = self.repos.oldest_rev94 current_rev = self.repos.normalize_rev(current_rev)95 except TracError:96 current_rev = None97 while current_rev is not None:98 changeset = self.repos.get_changeset(current_rev)99 cursor.execute("INSERT INTO revision (rev,time,author,message) "100 "VALUES (%s,%s,%s,%s)", (str(current_rev),101 to_timestamp(changeset.date),102 changeset.author,103 changeset.message))104 for path,kind,action,base_path,base_rev in changeset.get_changes():105 self.log.debug("Caching node change in [%s]: %s"106 % (current_rev, (path, kind, action,107 base_path, base_rev)))108 kind = kindmap[kind]109 action = actionmap[action]110 cursor.execute("INSERT INTO node_change (rev,path,"111 "node_type,change_type,base_path,base_rev) "112 "VALUES (%s,%s,%s,%s,%s,%s)",113 (str(current_rev), path, kind, action,114 base_path, base_rev))115 current_rev = self.repos.next_rev(current_rev)116 self.db.commit()117 self.repos.authz = authz # restore permission checking118 133 134 try: 135 while next_youngest is not None: 136 137 # 1.1 Attempt to resync the 'revision' table 138 self.log.info("Trying to sync revision [%s]" % 139 next_youngest) 140 cset = self.repos.get_changeset(next_youngest) 141 try: 142 cursor.execute("INSERT INTO revision " 143 " (rev,time,author,message) " 144 "VALUES (%s,%s,%s,%s)", 145 (str(next_youngest), 146 to_timestamp(cset.date), 147 cset.author, cset.message)) 148 self.db.commit() 149 except Exception, e: # *another* 1.1. resync attempt won 150 self.log.warning('Revision %s already cached: %s' % 151 (next_youngest, e)) 152 # also potentially in progress, so keep ''previous'' 153 # notion of 'youngest' 154 return 155 156 # 1.2. now *only* one process was able to get there 157 # (i.e. there *shouldn't* be any race condition here) 158 159 for path,kind,action,bpath,brev in cset.get_changes(): 160 self.log.debug("Caching node change in [%s]: %s" 161 % (next_youngest, 162 (path,kind,action,bpath,brev))) 163 kind = kindmap[kind] 164 action = actionmap[action] 165 cursor.execute("INSERT INTO node_change " 166 " (rev,path,node_type,change_type, " 167 " base_path,base_rev) " 168 "VALUES (%s,%s,%s,%s,%s,%s)", 169 (str(next_youngest), 170 path, kind, action, bpath, brev)) 171 172 # 1.3. iterate (1.1 should always succeed now) 173 self.youngest = next_youngest 174 next_youngest = self.repos.next_rev(next_youngest) 175 176 # 2. update 'youngest_rev' metadata (minimize failures at 0.) 177 cursor.execute("UPDATE system SET value=%s WHERE name=%s", 178 (str(self.youngest), CACHE_YOUNGEST_REV)) 179 self.db.commit() 180 finally: 181 # 3. restore permission checking (after 1.) 182 self.repos.authz = authz 183 119 184 def get_node(self, path, rev=None): 120 185 return self.repos.get_node(path, rev) 121 186 … … 126 191 return self.repos.oldest_rev 127 192 128 193 def get_youngest_rev(self): 129 return self. repos.get_youngest_rev_in_cache(self.db)194 return self.youngest 130 195 131 196 def previous_rev(self, rev): 132 197 return self.repos.previous_rev(rev) -
trac/admin/console.py
600 600 cursor.execute("DELETE FROM revision") 601 601 cursor.execute("DELETE FROM node_change") 602 602 cursor.execute("DELETE FROM system WHERE name='repository_dir'") 603 cursor.execute("DELETE FROM system WHERE name='youngest_rev'") 604 cursor.execute("INSERT INTO system (name, value) " 605 "VALUES ('youngest_rev', '')") 603 606 repos = self.__env.get_repository() # this will do the sync() 604 607 print 'Done.' 605 608
