Mercurial > kallithea
comparison rhodecode/model/scm.py @ 1038:5554aa9c2480 beta
another major code rafactor, reimplemented (almost from scratch)
the way caching works, Should be solid rock for now. Some code optymizations on scmModel.get() to make it don't load unneded things. Changed db cache to file that should also reduce memory size
author | Marcin Kuzminski <marcin@python-works.com> |
---|---|
date | Sun, 13 Feb 2011 00:29:31 +0100 |
parents | e2ebbb27df4e |
children | 51b70569c330 |
comparison
equal
deleted
inserted
replaced
1037:b1d6478d4561 | 1038:5554aa9c2480 |
---|---|
29 import traceback | 29 import traceback |
30 import logging | 30 import logging |
31 | 31 |
32 from mercurial import ui | 32 from mercurial import ui |
33 | 33 |
34 from sqlalchemy.orm import joinedload | |
35 from sqlalchemy.orm.session import make_transient | |
36 from sqlalchemy.exc import DatabaseError | 34 from sqlalchemy.exc import DatabaseError |
37 | 35 |
38 from beaker.cache import cache_region, region_invalidate | 36 from beaker.cache import cache_region, region_invalidate |
39 | 37 |
40 from vcs import get_backend | 38 from vcs import get_backend |
43 from vcs.utils.lazy import LazyProperty | 41 from vcs.utils.lazy import LazyProperty |
44 | 42 |
45 from rhodecode import BACKENDS | 43 from rhodecode import BACKENDS |
46 from rhodecode.lib import helpers as h | 44 from rhodecode.lib import helpers as h |
47 from rhodecode.lib.auth import HasRepoPermissionAny | 45 from rhodecode.lib.auth import HasRepoPermissionAny |
48 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, action_logger | 46 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \ |
47 action_logger | |
49 from rhodecode.model import BaseModel | 48 from rhodecode.model import BaseModel |
50 from rhodecode.model.user import UserModel | 49 from rhodecode.model.user import UserModel |
50 from rhodecode.model.repo import RepoModel | |
51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \ | 51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \ |
52 UserFollowing, UserLog | 52 UserFollowing, UserLog |
53 from rhodecode.model.caching_query import FromCache | 53 from rhodecode.model.caching_query import FromCache |
54 | 54 |
55 log = logging.getLogger(__name__) | 55 log = logging.getLogger(__name__) |
80 | 80 |
81 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one() | 81 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one() |
82 | 82 |
83 return q.ui_value | 83 return q.ui_value |
84 | 84 |
85 def repo_scan(self, repos_path, baseui): | 85 def repo_scan(self, repos_path=None): |
86 """Listing of repositories in given path. This path should not be a | 86 """Listing of repositories in given path. This path should not be a |
87 repository itself. Return a dictionary of repository objects | 87 repository itself. Return a dictionary of repository objects |
88 | 88 |
89 :param repos_path: path to directory containing repositories | 89 :param repos_path: path to directory containing repositories |
90 :param baseui: baseui instance to instantiate MercurialRepostitory with | |
91 """ | 90 """ |
92 | 91 |
93 log.info('scanning for repositories in %s', repos_path) | 92 log.info('scanning for repositories in %s', repos_path) |
94 | 93 |
95 if not isinstance(baseui, ui.ui): | 94 if repos_path is None: |
96 baseui = make_ui('db') | 95 repos_path = self.repos_path |
96 | |
97 baseui = make_ui('db') | |
97 repos_list = {} | 98 repos_list = {} |
98 | 99 |
99 for name, path in get_filesystem_repos(repos_path, recursive=True): | 100 for name, path in get_filesystem_repos(repos_path, recursive=True): |
100 try: | 101 try: |
101 if repos_list.has_key(name): | 102 if repos_list.has_key(name): |
132 .filter(CacheInvalidation.cache_active == False)\ | 133 .filter(CacheInvalidation.cache_active == False)\ |
133 .all()] | 134 .all()] |
134 | 135 |
135 for r in all_repos: | 136 for r in all_repos: |
136 | 137 |
137 repo = self.get(r.repo_name, invalidation_list) | 138 repo, dbrepo = self.get(r.repo_name, invalidation_list) |
138 | 139 |
139 if repo is not None: | 140 if repo is not None: |
140 last_change = repo.last_change | 141 last_change = repo.last_change |
141 tip = h.get_changeset_safe(repo, 'tip') | 142 tip = h.get_changeset_safe(repo, 'tip') |
142 | 143 |
143 tmp_d = {} | 144 tmp_d = {} |
144 tmp_d['name'] = r.repo_name | 145 tmp_d['name'] = r.repo_name |
145 tmp_d['name_sort'] = tmp_d['name'].lower() | 146 tmp_d['name_sort'] = tmp_d['name'].lower() |
146 tmp_d['description'] = repo.dbrepo.description | 147 tmp_d['description'] = dbrepo.description |
147 tmp_d['description_sort'] = tmp_d['description'] | 148 tmp_d['description_sort'] = tmp_d['description'] |
148 tmp_d['last_change'] = last_change | 149 tmp_d['last_change'] = last_change |
149 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple()) | 150 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple()) |
150 tmp_d['tip'] = tip.raw_id | 151 tmp_d['tip'] = tip.raw_id |
151 tmp_d['tip_sort'] = tip.revision | 152 tmp_d['tip_sort'] = tip.revision |
152 tmp_d['rev'] = tip.revision | 153 tmp_d['rev'] = tip.revision |
153 tmp_d['contact'] = repo.dbrepo.user.full_contact | 154 tmp_d['contact'] = dbrepo.user.full_contact |
154 tmp_d['contact_sort'] = tmp_d['contact'] | 155 tmp_d['contact_sort'] = tmp_d['contact'] |
155 tmp_d['owner_sort'] = tmp_d['contact'] | 156 tmp_d['owner_sort'] = tmp_d['contact'] |
156 tmp_d['repo_archives'] = list(repo._get_archives()) | 157 tmp_d['repo_archives'] = list(repo._get_archives()) |
157 tmp_d['last_msg'] = tip.message | 158 tmp_d['last_msg'] = tip.message |
158 tmp_d['repo'] = repo | 159 tmp_d['repo'] = repo |
160 tmp_d['dbrepo'] = dbrepo | |
159 yield tmp_d | 161 yield tmp_d |
160 | 162 |
161 def get_repo(self, repo_name): | 163 def get(self, repo_name, invalidation_list=None, retval='all'): |
162 return self.get(repo_name) | 164 """Returns a tuple of Repository,DbRepository, |
163 | 165 Get's repository from given name, creates BackendInstance and |
164 def get(self, repo_name, invalidation_list=None): | |
165 """Get's repository from given name, creates BackendInstance and | |
166 propagates it's data from database with all additional information | 166 propagates it's data from database with all additional information |
167 | 167 |
168 :param repo_name: | 168 :param repo_name: |
169 :param invalidation_list: if a invalidation list is given the get | 169 :param invalidation_list: if a invalidation list is given the get |
170 method should not manually check if this repository needs | 170 method should not manually check if this repository needs |
171 invalidation and just invalidate the repositories in list | 171 invalidation and just invalidate the repositories in list |
172 | 172 :param retval: string specifing what to return one of 'repo','dbrepo', |
173 'all'if repo or dbrepo is given it'll just lazy load chosen type | |
174 and return None as the second | |
173 """ | 175 """ |
174 if not HasRepoPermissionAny('repository.read', 'repository.write', | 176 if not HasRepoPermissionAny('repository.read', 'repository.write', |
175 'repository.admin')(repo_name, 'get repo check'): | 177 'repository.admin')(repo_name, 'get repo check'): |
176 return | 178 return |
177 | 179 |
187 alias = get_scm(repo_path)[0] | 189 alias = get_scm(repo_path)[0] |
188 log.debug('Creating instance of %s repository', alias) | 190 log.debug('Creating instance of %s repository', alias) |
189 backend = get_backend(alias) | 191 backend = get_backend(alias) |
190 except VCSError: | 192 except VCSError: |
191 log.error(traceback.format_exc()) | 193 log.error(traceback.format_exc()) |
192 log.error('Perhaps this repository is in db and not in filesystem' | 194 log.error('Perhaps this repository is in db and not in ' |
193 'run rescan repositories with "destroy old data "' | 195 'filesystem run rescan repositories with ' |
194 'option from admin panel') | 196 '"destroy old data " option from admin panel') |
195 return | 197 return |
196 | 198 |
197 if alias == 'hg': | 199 if alias == 'hg': |
198 from pylons import app_globals as g | 200 repo = backend(repo_path, create=False, baseui=make_ui('db')) |
199 repo = backend(repo_path, create=False, baseui=g.baseui) | |
200 #skip hidden web repository | 201 #skip hidden web repository |
201 if repo._get_hidden(): | 202 if repo._get_hidden(): |
202 return | 203 return |
203 else: | 204 else: |
204 repo = backend(repo_path, create=False) | 205 repo = backend(repo_path, create=False) |
205 | 206 |
206 dbrepo = self.sa.query(Repository)\ | |
207 .options(joinedload(Repository.fork))\ | |
208 .options(joinedload(Repository.user))\ | |
209 .filter(Repository.repo_name == repo_name)\ | |
210 .scalar() | |
211 | |
212 self.sa.expunge_all() | |
213 log.debug('making transient %s', dbrepo) | |
214 make_transient(dbrepo) | |
215 | |
216 for attr in ['user', 'forks', 'followers', 'group', 'repo_to_perm', | |
217 'users_group_to_perm', 'stats', 'logs']: | |
218 attr = getattr(dbrepo, attr, False) | |
219 if attr: | |
220 if isinstance(attr, list): | |
221 for a in attr: | |
222 log.debug('making transient %s', a) | |
223 make_transient(a) | |
224 else: | |
225 log.debug('making transient %s', attr) | |
226 make_transient(attr) | |
227 | |
228 repo.dbrepo = dbrepo | |
229 return repo | 207 return repo |
230 | 208 |
231 pre_invalidate = True | 209 pre_invalidate = True |
210 dbinvalidate = False | |
211 | |
232 if invalidation_list is not None: | 212 if invalidation_list is not None: |
233 pre_invalidate = repo_name in invalidation_list | 213 pre_invalidate = repo_name in invalidation_list |
234 | 214 |
235 if pre_invalidate: | 215 if pre_invalidate: |
216 #this returns object to invalidate | |
236 invalidate = self._should_invalidate(repo_name) | 217 invalidate = self._should_invalidate(repo_name) |
237 | |
238 if invalidate: | 218 if invalidate: |
239 log.info('invalidating cache for repository %s', repo_name) | 219 log.info('invalidating cache for repository %s', repo_name) |
240 region_invalidate(_get_repo, None, repo_name) | 220 #region_invalidate(_get_repo, None, repo_name) |
241 self._mark_invalidated(invalidate) | 221 self._mark_invalidated(invalidate) |
242 | 222 dbinvalidate = True |
243 return _get_repo(repo_name) | 223 |
224 r, dbr = None, None | |
225 if retval == 'repo' or 'all': | |
226 r = _get_repo(repo_name) | |
227 if retval == 'dbrepo' or 'all': | |
228 dbr = RepoModel(self.sa).get_full(repo_name, cache=True, | |
229 invalidate=dbinvalidate) | |
230 | |
231 | |
232 return r, dbr | |
244 | 233 |
245 | 234 |
246 | 235 |
247 def mark_for_invalidation(self, repo_name): | 236 def mark_for_invalidation(self, repo_name): |
248 """Puts cache invalidation task into db for | 237 """Puts cache invalidation task into db for |
368 | 357 |
369 :param repo_name: | 358 :param repo_name: |
370 """ | 359 """ |
371 | 360 |
372 ret = self.sa.query(CacheInvalidation)\ | 361 ret = self.sa.query(CacheInvalidation)\ |
373 .options(FromCache('sql_cache_short', | |
374 'get_invalidation_%s' % repo_name))\ | |
375 .filter(CacheInvalidation.cache_key == repo_name)\ | 362 .filter(CacheInvalidation.cache_key == repo_name)\ |
376 .filter(CacheInvalidation.cache_active == False)\ | 363 .filter(CacheInvalidation.cache_active == False)\ |
377 .scalar() | 364 .scalar() |
378 | 365 |
379 return ret | 366 return ret |
380 | 367 |
381 def _mark_invalidated(self, cache_key): | 368 def _mark_invalidated(self, cache_key): |
382 """ Marks all occurences of cache to invaldation as already invalidated | 369 """ Marks all occurrences of cache to invalidation as already |
370 invalidated | |
383 | 371 |
384 :param cache_key: | 372 :param cache_key: |
385 """ | 373 """ |
386 | 374 |
387 if cache_key: | 375 if cache_key: |