TracDev/Proposals/CacheInvalidation: cache_control-r7933.diff
| File cache_control-r7933.diff, 5.6 KB (added by cboos, 3 years ago) |
|---|
-
trac/env.py
Proof-of-concept implementation of cache control diff --git a/trac/env.py b/trac/env.py
a b 196 196 ('setuptools', setuptools.__version__), 197 197 ] 198 198 self._href = self._abs_href = None 199 self._cache_control = {} 200 self._cache_lock = threading.RLock() 199 201 200 202 from trac.loader import load_components 201 203 plugins_dir = self.config.get('inherit', 'plugins_dir') … … 481 483 return self._abs_href 482 484 abs_href = property(_get_abs_href, 'The application URL') 483 485 486 # Cache control 487 488 def update_cache_control(self): 489 self._cache_lock.acquire() 490 try: 491 db = self.get_db_cnx() 492 cursor = db.cursor() 493 cursor.execute("SELECT key, generation FROM cache_control") 494 for key, gen in cursor.fetchall(): 495 previous_gen = self._cache_control.get(key, 0) 496 if abs(previous_gen) < gen: 497 self._cache_control[key] = -gen 498 self.log.debug("Cache '%s' needs update (%d)", key, gen) 499 # otherwise a race happened, a newer generation already made it 500 # into the cache control: 501 # - either it's positive, meaning the actual cache was 502 # already updated to a newer generation, 503 # - or it's negative meaning we already target a newer 504 # generation. 505 # In both cases we simply do nothing. 506 finally: 507 self._cache_lock.release() 508 509 def invalidate_cache(self, key): 510 if not isinstance(key, basestring): 511 key = key.__class__.__name__ 512 self._cache_lock.acquire() 513 try: 514 db = self.get_db_cnx() 515 cursor = db.cursor() 516 try: 517 gen = 1 518 cursor.execute("INSERT INTO cache_control VALUES (%s,%s)", 519 (key, gen)) 520 except: 521 cursor.execute("UPDATE cache_control SET " 522 "generation=generation+1 WHERE key=%s ", (key,)) 523 cursor.execute("SELECT generation FROM cache_control " 524 "WHERE key=%s", (key,)) 525 cursor.fetchone() 526 for gen, in cursor: 527 break 528 db.commit() 529 self._cache_control[key] = -gen # negative means invalid 530 self.log.debug("Cache '%s' invalidated (generation %d)", key, gen) 531 finally: 532 self._cache_lock.release() 533 534 def cache_is_valid(self, key): 535 if not isinstance(key, basestring): 536 key = key.__class__.__name__ 537 # Not sure it's worth locking here: in case of a a concurrent 538 # invalidation, even if we use the lock, we risk to miss the 539 # notification for this request 540 return self._cache_control.get(key, 0) >= 0 # negative means invalid 541 542 def validate_cache(self, key): 543 if not isinstance(key, basestring): 544 key = key.__class__.__name__ 545 self._cache_lock.acquire() 546 try: 547 gen = abs(self._cache_control.get(key, 1)) 548 self._cache_control[key] = gen 549 self.log.debug("Cache '%s' updated to generation %d", key, gen) 550 finally: 551 self._cache_lock.release() 552 553 484 554 485 555 class EnvironmentSetup(Component): 486 556 implements(IEnvironmentSetupParticipant) … … 583 653 env = None 584 654 if env is None: 585 655 env = env_cache.setdefault(env_path, open_environment(env_path)) 656 else: 657 env.update_cache_control() 586 658 finally: 587 659 env_cache_lock.release() 588 660 else: -
trac/wiki/interwiki.py
diff --git a/trac/wiki/interwiki.py b/trac/wiki/interwiki.py
a b 44 44 45 45 def reset(self): 46 46 self._interwiki_map = None 47 self.config.touch()48 # This dictionary maps upper-cased namespaces49 # to (namespace, prefix, title) values;47 # The above dictionary maps upper-cased namespaces 48 # to (namespace, prefix, title) values 49 self.env.invalidate_cache(self) 50 50 51 51 # The component itself behaves as a map 52 52 … … 114 114 115 115 def _get_interwiki_map(self): 116 116 from trac.wiki.model import WikiPage 117 if self._interwiki_map is None :117 if self._interwiki_map is None or not self.env.cache_is_valid(self): 118 118 self._interwiki_lock.acquire() 119 119 try: 120 if self._interwiki_map is None :120 if self._interwiki_map is None or not self.env.cache_is_valid(self): 121 121 self._interwiki_map = {} 122 122 content = WikiPage(self.env, InterWikiMap._page_name).text 123 123 in_map = False … … 131 131 prefix, url, title = m.groups() 132 132 url = url.strip() 133 133 title = title and title.strip() or prefix 134 self[prefix] = (prefix, url, title) 134 self._interwiki_map[prefix.upper()] = \ 135 (prefix, url, title) 135 136 elif line.startswith('----'): 136 137 in_map = True 138 self.env.validate_cache(self) 137 139 finally: 138 140 self._interwiki_lock.release() 139 141 return self._interwiki_map
