175 lines
6.7 KiB
Python
175 lines
6.7 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from odoo.tests.common import TransactionCase
|
||
|
from odoo.tools import get_cache_key_counter
|
||
|
from threading import Thread, Barrier
|
||
|
|
||
|
class TestOrmcache(TransactionCase):
|
||
|
def test_ormcache(self):
|
||
|
""" Test the effectiveness of the ormcache() decorator. """
|
||
|
IMD = self.env['ir.model.data']
|
||
|
XMLID = 'base.group_no_one'
|
||
|
|
||
|
# retrieve the cache, its key and stat counter
|
||
|
cache, key, counter = get_cache_key_counter(IMD._xmlid_lookup, XMLID)
|
||
|
hit = counter.hit
|
||
|
miss = counter.miss
|
||
|
|
||
|
# clear the caches of ir.model.data, retrieve its key and
|
||
|
self.env.registry.clear_cache()
|
||
|
self.assertNotIn(key, cache)
|
||
|
|
||
|
# lookup some reference
|
||
|
self.env.ref(XMLID)
|
||
|
self.assertEqual(counter.hit, hit)
|
||
|
self.assertEqual(counter.miss, miss + 1)
|
||
|
self.assertIn(key, cache)
|
||
|
|
||
|
# lookup again
|
||
|
self.env.ref(XMLID)
|
||
|
self.assertEqual(counter.hit, hit + 1)
|
||
|
self.assertEqual(counter.miss, miss + 1)
|
||
|
self.assertIn(key, cache)
|
||
|
|
||
|
# lookup again
|
||
|
self.env.ref(XMLID)
|
||
|
self.assertEqual(counter.hit, hit + 2)
|
||
|
self.assertEqual(counter.miss, miss + 1)
|
||
|
self.assertIn(key, cache)
|
||
|
|
||
|
def test_invalidation(self):
|
||
|
self.assertEqual(self.env.registry.cache_invalidated, set())
|
||
|
self.env.registry.clear_cache()
|
||
|
self.env.registry.clear_cache('templates')
|
||
|
self.assertEqual(self.env.registry.cache_invalidated, {'default', 'templates'})
|
||
|
self.env.registry.reset_changes()
|
||
|
self.assertEqual(self.env.registry.cache_invalidated, set())
|
||
|
self.env.registry.clear_cache('assets')
|
||
|
self.assertEqual(self.env.registry.cache_invalidated, {'assets'})
|
||
|
self.env.registry.reset_changes()
|
||
|
self.assertEqual(self.env.registry.cache_invalidated, set())
|
||
|
|
||
|
def test_invalidation_thread_local(self):
|
||
|
# this test ensures that the registry.cache_invalidated set is thread local
|
||
|
|
||
|
caches = ['default', 'templates', 'assets']
|
||
|
nb_treads = len(caches)
|
||
|
|
||
|
# use barriers to ensure threads synchronization
|
||
|
sync_clear_cache = Barrier(nb_treads, timeout=5)
|
||
|
sync_assert_equal = Barrier(nb_treads, timeout=5)
|
||
|
sync_reset = Barrier(nb_treads, timeout=5)
|
||
|
|
||
|
operations = []
|
||
|
def run(cache):
|
||
|
self.assertEqual(self.env.registry.cache_invalidated, set())
|
||
|
|
||
|
self.env.registry.clear_cache(cache)
|
||
|
operations.append('clear_cache')
|
||
|
sync_clear_cache.wait()
|
||
|
|
||
|
self.assertEqual(self.env.registry.cache_invalidated, {cache})
|
||
|
operations.append('assert_contains')
|
||
|
sync_assert_equal.wait()
|
||
|
|
||
|
self.env.registry.reset_changes()
|
||
|
operations.append('reset_changes')
|
||
|
sync_reset.wait()
|
||
|
|
||
|
self.assertEqual(self.env.registry.cache_invalidated, set())
|
||
|
operations.append('assert_empty')
|
||
|
|
||
|
# run all threads
|
||
|
threads = []
|
||
|
for cache in caches:
|
||
|
threads.append(Thread(target=run, args=(cache,)))
|
||
|
for thread in threads:
|
||
|
thread.start()
|
||
|
for thread in threads:
|
||
|
thread.join()
|
||
|
|
||
|
# ensure that the threads operations where executed in the expected order
|
||
|
self.assertEqual(
|
||
|
operations,
|
||
|
['clear_cache'] * nb_treads +
|
||
|
['assert_contains'] * nb_treads +
|
||
|
['reset_changes'] * nb_treads +
|
||
|
['assert_empty'] * nb_treads
|
||
|
)
|
||
|
|
||
|
def test_signaling_01_single(self):
|
||
|
self.assertFalse(self.registry.test_cr)
|
||
|
self.registry.cache_invalidated.clear()
|
||
|
registry = self.registry
|
||
|
old_sequences = dict(registry.cache_sequences)
|
||
|
with self.assertLogs('odoo.modules.registry') as logs:
|
||
|
registry.cache_invalidated.add('assets')
|
||
|
self.assertEqual(registry.cache_invalidated, {'assets'})
|
||
|
registry.signal_changes()
|
||
|
self.assertFalse(registry.cache_invalidated)
|
||
|
|
||
|
self.assertEqual(
|
||
|
logs.output,
|
||
|
["INFO:odoo.modules.registry:Caches invalidated, signaling through the database: ['assets']"],
|
||
|
)
|
||
|
|
||
|
for key, value in old_sequences.items():
|
||
|
if key == 'assets':
|
||
|
self.assertEqual(value + 1, registry.cache_sequences[key], "Assets cache sequence should have changed")
|
||
|
else:
|
||
|
self.assertEqual(value, registry.cache_sequences[key], "other registry sequence shouldn't have changed")
|
||
|
|
||
|
with self.assertNoLogs(None, None): # the registry sequence should be up to date on the same worker
|
||
|
registry.check_signaling()
|
||
|
|
||
|
# simulate other worker state
|
||
|
|
||
|
registry.cache_sequences.update(old_sequences)
|
||
|
|
||
|
with self.assertLogs() as logs:
|
||
|
registry.check_signaling()
|
||
|
self.assertEqual(
|
||
|
logs.output,
|
||
|
["INFO:odoo.modules.registry:Invalidating caches after database signaling: ['assets', 'templates.cached_values']"],
|
||
|
)
|
||
|
|
||
|
def test_signaling_01_multiple(self):
|
||
|
self.assertFalse(self.registry.test_cr)
|
||
|
self.registry.cache_invalidated.clear()
|
||
|
registry = self.registry
|
||
|
old_sequences = dict(registry.cache_sequences)
|
||
|
with self.assertLogs('odoo.modules.registry') as logs:
|
||
|
registry.cache_invalidated.add('assets')
|
||
|
registry.cache_invalidated.add('default')
|
||
|
self.assertEqual(registry.cache_invalidated, {'assets', 'default'})
|
||
|
registry.signal_changes()
|
||
|
self.assertFalse(registry.cache_invalidated)
|
||
|
|
||
|
self.assertEqual(
|
||
|
logs.output,
|
||
|
[
|
||
|
"INFO:odoo.modules.registry:Caches invalidated, signaling through the database: ['assets', 'default']",
|
||
|
],
|
||
|
)
|
||
|
|
||
|
for key, value in old_sequences.items():
|
||
|
if key in ('assets', 'default'):
|
||
|
self.assertEqual(value + 1, registry.cache_sequences[key], "Assets and default cache sequence should have changed")
|
||
|
else:
|
||
|
self.assertEqual(value, registry.cache_sequences[key], "other registry sequence shouldn't have changed")
|
||
|
|
||
|
with self.assertNoLogs(None, None): # the registry sequence should be up to date on the same worker
|
||
|
registry.check_signaling()
|
||
|
|
||
|
# simulate other worker state
|
||
|
|
||
|
registry.cache_sequences.update(old_sequences)
|
||
|
|
||
|
with self.assertLogs() as logs:
|
||
|
registry.check_signaling()
|
||
|
self.assertEqual(
|
||
|
logs.output,
|
||
|
["INFO:odoo.modules.registry:Invalidating caches after database signaling: ['assets', 'default', 'templates.cached_values']"],
|
||
|
)
|