Add a setting controlling whether cache-machine is enabled or not

- Also, use that setting to disable cache-machine by default in tests,
  locally and on dev.
- Tweak translations tests to not depend on cache-machine at all
  (remove dependency on ModelBase, depend on django models only
   + the translation transformer)
This commit is contained in:
Mathieu Pillard 2018-04-25 11:43:56 +02:00
Родитель a31cd814f7
Коммит c3e6d3cc1f
10 изменённых файлов: 52 добавлений и 35 удалений

Просмотреть файл

@ -20,6 +20,9 @@ INSTALLED_APPS += (
FILESYSTEM_CACHE_ROOT = os.path.join(TMP_PATH, 'cache')
# Disable cache-machine locally and in tests to prepare for its removal.
CACHE_MACHINE_ENABLED = False
# Using locmem deadlocks in certain scenarios. This should all be fixed,
# hopefully, in Django1.7. At that point, we may try again, and remove this to
# not require memcache installation for newcomers.

Просмотреть файл

@ -951,4 +951,4 @@ class CompatOverrideView(ListAPIView):
raise exceptions.ParseError(
'Empty, or no, guid parameter provided.')
return queryset.filter(guid__in=guids).transform(
CompatOverride.transformer)
CompatOverride.transformer).order_by('-pk')

Просмотреть файл

@ -23,8 +23,11 @@ from olympia.translations.hold import save_translations
from . import search
# Store whether we should be skipping cache-machine for this thread or not.
# Note that because the value is initialized at import time, we can't use
# override_settings() on CACHE_MACHINE_ENABLED.
_locals = threading.local()
_locals.skip_cache = False
_locals.skip_cache = not settings.CACHE_MACHINE_ENABLED
log = olympia.core.logger.getLogger('z.addons')
@ -43,7 +46,7 @@ def use_master():
@contextlib.contextmanager
def skip_cache():
"""Within this context, no queries come from cache."""
old = getattr(_locals, 'skip_cache', False)
old = getattr(_locals, 'skip_cache', not settings.CACHE_MACHINE_ENABLED)
_locals.skip_cache = True
try:
yield

Просмотреть файл

@ -1,6 +1,7 @@
import mock
import pytest
from django.conf import settings
from django.core.files.storage import default_storage as storage
from mock import Mock
@ -29,14 +30,22 @@ class ManualOrderTest(TestCase):
def test_skip_cache():
assert not getattr(amo_models._locals, 'skip_cache', False)
assert (
getattr(amo_models._locals, 'skip_cache') is
not settings.CACHE_MACHINE_ENABLED)
setattr(amo_models._locals, 'skip_cache', False)
with amo_models.skip_cache():
assert amo_models._locals.skip_cache
with amo_models.skip_cache():
assert amo_models._locals.skip_cache
assert amo_models._locals.skip_cache
assert not amo_models._locals.skip_cache
setattr(amo_models._locals, 'skip_cache', settings.CACHE_MACHINE_ENABLED)
def test_use_master():
local = amo_models.multidb.pinning._locals

Просмотреть файл

@ -84,6 +84,9 @@ SLAVE_DATABASES = ['slave']
CACHE_MIDDLEWARE_KEY_PREFIX = CACHE_PREFIX
# Disable cache-machine on dev to prepare for its removal.
CACHE_MACHINE_ENABLED = False
CACHES = {
'filesystem': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',

Просмотреть файл

@ -1006,6 +1006,8 @@ MINIFY_BUNDLES = {
# Caching
CACHE_MACHINE_ENABLED = True
# Prefix for cache keys (will prevent collisions when running parallel copies)
CACHE_PREFIX = 'amo:%s:' % build_id
KEY_PREFIX = CACHE_PREFIX

Просмотреть файл

@ -789,20 +789,18 @@ class TestRatingViewSetGet(TestCase):
assert Rating.unfiltered.count() == 3
cache.clear()
with self.assertNumQueries(7):
# 7 queries:
# - One for the reviews count
# - One for the reviews ids (cache-machine FETCH_BY_ID)
# - One for the reviews fields
# - One for the reviews translations
with self.assertNumQueries(6):
# 6 queries:
# - One for the ratings count (pagination)
# - One for the ratings themselves
# - One for the ratings translations
# - One for the replies (there aren't any, but we don't know
# that without making a query)
# - Two for opening and closing a transaction/savepoint
# (https://github.com/mozilla/addons-server/issues/3610)
#
# We patch get_addon_object() to avoid the add-on related queries,
# which would pollute the result. In the real world those queries
# would often be in the cache.
# which would pollute the result.
with mock.patch('olympia.ratings.views.RatingViewSet'
'.get_addon_object') as get_addon_object:
get_addon_object.return_value = self.addon
@ -840,15 +838,13 @@ class TestRatingViewSetGet(TestCase):
assert Rating.unfiltered.count() == 5
cache.clear()
with self.assertNumQueries(9):
with self.assertNumQueries(7):
# 9 queries:
# - One for the reviews count
# - One for the reviews ids (cache-machine FETCH_BY_ID)
# - One for the reviews fields
# - One for the reviews translations
# - One for the replies ids
# - One for the replies fields
# - One for the replies translations
# - One for the ratings count
# - One for the ratings
# - One for the ratings translations
# - One for the ratings fields
# - One for the ratings translations
# - Two for opening and closing a transaction/savepoint
# (https://github.com/mozilla/addons-server/issues/3610)
#

Просмотреть файл

@ -744,7 +744,7 @@ class TestReviewerScore(TestCase):
with self.assertNumQueries(0):
ReviewerScore.get_recent(self.user)
with self.assertNumQueries(3):
with self.assertNumQueries(2):
ReviewerScore.get_leaderboards(self.user)
with self.assertNumQueries(0):
ReviewerScore.get_leaderboards(self.user)
@ -761,7 +761,7 @@ class TestReviewerScore(TestCase):
ReviewerScore.get_total(self.user)
with self.assertNumQueries(1):
ReviewerScore.get_recent(self.user)
with self.assertNumQueries(3):
with self.assertNumQueries(2):
ReviewerScore.get_leaderboards(self.user)
with self.assertNumQueries(1):
ReviewerScore.get_breakdown(self.user)

Просмотреть файл

@ -412,10 +412,10 @@ class TranslationTestCase(BaseTestCase):
obj.description.delete()
obj = TranslatedModel.objects.no_cache().get(id=1)
obj = TranslatedModel.objects.get(id=1)
assert obj.description_id is None
assert obj.description is None
assert not Translation.objects.no_cache().filter(id=trans_id).exists()
assert not Translation.objects.filter(id=trans_id).exists()
@patch.object(TranslatedModel, 'get_fallback', create=True)
def test_delete_keep_other_translations(self, get_fallback):
@ -431,9 +431,8 @@ class TranslationTestCase(BaseTestCase):
obj.name.delete()
obj = TranslatedModel.objects.no_cache().get(id=1)
assert Translation.objects.no_cache().filter(
id=orig_name_id).count() == 1
obj = TranslatedModel.objects.get(id=1)
assert Translation.objects.filter(id=orig_name_id).count() == 1
# We shouldn't have set name_id to None.
assert obj.name_id == orig_name_id
@ -481,7 +480,7 @@ class TranslationMultiDbTests(TransactionTestCase):
# Make sure we are in a clean environnement.
self.reset_queries()
TranslatedModel.objects.get(pk=1)
assert len(connections['default'].queries) == 3
assert len(connections['default'].queries) == 2
@override_settings(DEBUG=True)
@patch('multidb.get_slave', lambda: 'slave-2')
@ -493,7 +492,7 @@ class TranslationMultiDbTests(TransactionTestCase):
TranslatedModel.objects.get(pk=1)
assert len(connections['default'].queries) == 0
assert len(connections['slave-1'].queries) == 0
assert len(connections['slave-2'].queries) == 3
assert len(connections['slave-2'].queries) == 2
@override_settings(DEBUG=True)
@patch('multidb.get_slave', lambda: 'slave-2')
@ -505,7 +504,7 @@ class TranslationMultiDbTests(TransactionTestCase):
TranslatedModel.objects.using('slave-1').get(pk=1)
assert len(connections['default'].queries) == 0
assert len(connections['slave-1'].queries) == 3
assert len(connections['slave-1'].queries) == 2
assert len(connections['slave-2'].queries) == 0
@override_settings(DEBUG=True)
@ -517,7 +516,7 @@ class TranslationMultiDbTests(TransactionTestCase):
with multidb.pinning.use_master:
TranslatedModel.objects.get(pk=1)
assert len(connections['default'].queries) == 3
assert len(connections['default'].queries) == 2
assert len(connections['slave-1'].queries) == 0
assert len(connections['slave-2'].queries) == 0

Просмотреть файл

@ -1,27 +1,29 @@
from django.db import models
from olympia.amo.models import ModelBase
from olympia.amo.models import UncachedManagerBase, UncachedModelBase
from olympia.translations.fields import (
LinkifiedField, PurifiedField, TranslatedField, save_signal)
class TranslatedModel(ModelBase):
class TranslatedModel(UncachedModelBase):
name = TranslatedField()
description = TranslatedField()
default_locale = models.CharField(max_length=10)
no_locale = TranslatedField(require_locale=False)
objects = UncachedManagerBase()
models.signals.pre_save.connect(save_signal, sender=TranslatedModel,
dispatch_uid='testapp_translatedmodel')
class UntranslatedModel(ModelBase):
class UntranslatedModel(UncachedModelBase):
"""Make sure nothing is broken when a model doesn't have translations."""
number = models.IntegerField()
class FancyModel(ModelBase):
class FancyModel(UncachedModelBase):
"""Mix it up with purified and linkified fields."""
purified = PurifiedField()
linkified = LinkifiedField()