Fixes #7743
This commit is contained in:
Christopher Grebs 2018-06-28 13:20:06 +02:00 коммит произвёл GitHub
Родитель f2ca175e59
Коммит ceae9d4fd0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
66 изменённых файлов: 167 добавлений и 450 удалений

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

@ -71,8 +71,6 @@ WORKDIR /data/olympia
# Install all python requires
RUN pip install --no-cache-dir --exists-action=w --no-deps -r requirements/system.txt \
&& pip install --no-cache-dir --exists-action=w --no-deps -r requirements/prod.txt \
&& [ -f requirements/prod_without_hash.txt ] \
&& pip install --no-cache-dir --exists-action=w --no-deps -r requirements/prod_without_hash.txt\
&& pip install --no-cache-dir --exists-action=w --no-deps -e .
# Link /usr/sbin/uwsgi and /usr/bin/uwsgi to deal with migration from Centos -> Debian

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

@ -101,7 +101,6 @@ install_python_common_dependencies:
# Can't use --progress-bar=off for system packages as long as our docker image
# doesn't have pip 10 by default.
pip install --no-deps --exists-action=w -r requirements/system.txt
pip install --progress-bar=off --no-deps --exists-action=w -r requirements/prod_without_hash.txt
install_python_test_dependencies: install_python_common_dependencies
pip install --progress-bar=off --no-deps --exists-action=w -r requirements/tests.txt

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

@ -3,7 +3,6 @@ from django.core.cache import caches
from django.utils import translation
import responses
import caching
import pytest
from multidb import pinning
@ -111,9 +110,6 @@ def default_prefixer(settings):
def test_pre_setup(request, tmpdir, settings):
caches['default'].clear()
caches['filesystem'].clear()
# Override django-cache-machine caching.base.TIMEOUT because it's
# computed too early, before settings_test.py is imported.
caching.base.TIMEOUT = settings.CACHE_COUNT_TIMEOUT
translation.trans_real.deactivate()
# Django fails to clear this cache.

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

@ -1,2 +0,0 @@
## Forked.
-e git+https://github.com/EnTeQuAk/django-cache-machine@1cac98a3e1dcc3cb654665cc67b6b15ab60d62c4#egg=django-cache-machine

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

@ -26,10 +26,6 @@ PAYPAL_PERMISSIONS_URL = ''
SITE_URL = 'http://testserver'
# COUNT() caching can't be invalidated, it just expires after x seconds. This
# is just too annoying for tests, so disable it.
CACHE_COUNT_TIMEOUT = -1
# We don't want to share cache state between processes. Always use the local
# memcache backend for tests.
#

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

@ -44,8 +44,6 @@ class GroupUser(models.Model):
del self.user.groups_list
except AttributeError:
pass
# Help cache-machine invalidate the group-related queries...
Group.objects.invalidate(self.group, *self.user.groups.all())
@dispatch.receiver(signals.post_save, sender=GroupUser,

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

@ -162,7 +162,6 @@ class PermissionsTestMixin(object):
# Add a second group membership to test duplicates
group2 = Group.objects.create(name='b', rules='Foo:Bar,Addons:Edit')
GroupUser.objects.create(group=group2, user=self.user)
Group.objects.invalidate(*Group.objects.all())
assert self.serializer(self.user).data['permissions'] == [
'Addons:Edit', 'Addons:Review', 'Foo:Bar', 'Personas:Review']

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

@ -128,7 +128,7 @@ def _change_last_updated(next):
log.debug('Updating %s add-ons' % len(changes))
# Update + invalidate.
qs = Addon.objects.no_cache().filter(id__in=changes).no_transforms()
qs = Addon.objects.filter(id__in=changes).no_transforms()
for addon in qs:
addon.update(last_updated=changes[addon.id])
@ -143,7 +143,7 @@ def addon_last_updated():
_change_last_updated(next)
# Get anything that didn't match above.
other = (Addon.objects.no_cache().filter(last_updated__isnull=True)
other = (Addon.objects.filter(last_updated__isnull=True)
.values_list('id', 'created'))
_change_last_updated(dict(other))
@ -191,7 +191,7 @@ def hide_disabled_files():
ids = (File.objects.filter(q | Q(status=amo.STATUS_DISABLED))
.values_list('id', flat=True))
for chunk in chunked(ids, 300):
qs = File.objects.no_cache().filter(id__in=chunk)
qs = File.objects.filter(id__in=chunk)
qs = qs.select_related('version')
for f in qs:
f.hide_disabled_file()
@ -237,7 +237,7 @@ def deliver_hotness():
one_week = now - timedelta(days=7)
four_weeks = now - timedelta(days=28)
for ids in chunked(all_ids, 300):
addons = Addon.objects.no_cache().filter(id__in=ids).no_transforms()
addons = Addon.objects.filter(id__in=ids).no_transforms()
ids = [a.id for a in addons if a.id not in frozen]
qs = (UpdateCount.objects.filter(addon__in=ids)
.values_list('addon').annotate(Avg('count')))

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

@ -556,7 +556,7 @@ class EditThemeForm(AddonFormBase):
super(AddonFormBase, self).__init__(*args, **kw)
addon = Addon.objects.no_cache().get(id=self.instance.id)
addon = Addon.objects.get(id=self.instance.id)
persona = addon.persona
# Allow theme artists to localize Name and Description.

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

@ -21,8 +21,6 @@ from django.utils.functional import cached_property
from django.utils import translation
from django.utils.translation import trans_real, ugettext_lazy as _
import caching.base as caching
from django_extensions.db.fields.json import JSONField
from django_statsd.clients import statsd
from jinja2.filters import do_dictsort
@ -36,7 +34,7 @@ from olympia.addons.utils import (
from olympia.amo.decorators import use_master, write
from olympia.amo.models import (
BasePreview, ManagerBase, manual_order, ModelBase, OnChangeMixin,
SaveUpdateMixin, SlugField)
SaveUpdateMixin, SlugField, BaseQuerySet)
from olympia.amo.templatetags import jinja_helpers
from olympia.amo.urlresolvers import reverse
from olympia.amo.utils import (
@ -150,7 +148,7 @@ def clean_slug(instance, slug_field='slug'):
return instance
class AddonQuerySet(caching.CachingQuerySet):
class AddonQuerySet(BaseQuerySet):
def id_or_slug(self, val):
"""Get add-ons by id or slug."""
if isinstance(val, basestring) and not val.isdigit():
@ -364,7 +362,7 @@ class Addon(OnChangeMixin, ModelBase):
# The order of those managers is very important:
# The first one discovered, if it has "use_for_related_fields = True"
# (which it has if it's inheriting from caching.base.CachingManager), will
# (which it has if it's inheriting `ManagerBase`), will
# be used for relations like `version.addon`. We thus want one that is NOT
# filtered in any case, we don't want a 500 if the addon is not found
# (because it has the status amo.STATUS_DELETED for example).
@ -723,7 +721,7 @@ class Addon(OnChangeMixin, ModelBase):
'channel': amo.RELEASE_CHANNEL_LISTED,
'files__status__in': statuses
}
return self.versions.no_cache().filter(**fltr).extra(
return self.versions.filter(**fltr).extra(
where=["""
NOT EXISTS (
SELECT 1 FROM files AS f2
@ -1012,7 +1010,7 @@ class Addon(OnChangeMixin, ModelBase):
if addon_dict is None:
addon_dict = dict((a.id, a) for a in addons)
qs = (UserProfile.objects.no_cache()
qs = (UserProfile.objects
.filter(addons__in=addons, addonuser__listed=True)
.extra(select={'addon_id': 'addons_users.addon_id',
'position': 'addons_users.position'}))
@ -1080,7 +1078,7 @@ class Addon(OnChangeMixin, ModelBase):
addons = [a for a in addons if a.type != amo.ADDON_PERSONA]
# Persona-specific stuff
for persona in Persona.objects.no_cache().filter(addon__in=personas):
for persona in Persona.objects.filter(addon__in=personas):
addon = addon_dict[persona.addon_id]
addon.persona = persona
@ -1290,19 +1288,19 @@ class Addon(OnChangeMixin, ModelBase):
"""
status_change = Max('versions__files__datestatuschanged')
public = (
Addon.objects.no_cache().filter(
Addon.objects.filter(
status=amo.STATUS_PUBLIC,
versions__files__status=amo.STATUS_PUBLIC)
.exclude(type=amo.ADDON_PERSONA)
.values('id').annotate(last_updated=status_change))
stati = amo.VALID_ADDON_STATUSES
exp = (Addon.objects.no_cache().exclude(status__in=stati)
exp = (Addon.objects.exclude(status__in=stati)
.filter(versions__files__status__in=amo.VALID_FILE_STATUSES)
.values('id')
.annotate(last_updated=Max('versions__files__created')))
personas = (Addon.objects.no_cache().filter(type=amo.ADDON_PERSONA)
personas = (Addon.objects.filter(type=amo.ADDON_PERSONA)
.extra(select={'last_updated': 'created'}))
return dict(public=public, exp=exp, personas=personas)
@ -1540,7 +1538,7 @@ class AddonReviewerFlags(ModelBase):
notified_about_expiring_info_request = models.BooleanField(default=False)
class Persona(caching.CachingMixin, models.Model):
class Persona(models.Model):
"""Personas-specific additions to the add-on model."""
STATUS_CHOICES = amo.STATUS_CHOICES_PERSONA
@ -1565,8 +1563,6 @@ class Persona(caching.CachingMixin, models.Model):
checksum = models.CharField(max_length=64, blank=True, default='')
dupe_persona = models.ForeignKey('self', null=True)
objects = caching.CachingManager()
class Meta:
db_table = 'personas'
@ -1739,14 +1735,12 @@ class MigratedLWT(OnChangeMixin, ModelBase):
self.getpersonas_id = self.lightweight_theme.persona.persona_id
class AddonCategory(caching.CachingMixin, models.Model):
class AddonCategory(models.Model):
addon = models.ForeignKey(Addon, on_delete=models.CASCADE)
category = models.ForeignKey('Category')
feature = models.BooleanField(default=False)
feature_locales = models.CharField(max_length=255, default='', null=True)
objects = caching.CachingManager()
class Meta:
db_table = 'addons_categories'
unique_together = ('addon', 'category')
@ -1756,8 +1750,7 @@ class AddonCategory(caching.CachingMixin, models.Model):
return get_creatured_ids(category, lang)
class AddonUser(caching.CachingMixin, OnChangeMixin, SaveUpdateMixin,
models.Model):
class AddonUser(OnChangeMixin, SaveUpdateMixin, models.Model):
addon = models.ForeignKey(Addon, on_delete=models.CASCADE)
user = UserForeignKey()
role = models.SmallIntegerField(default=amo.AUTHOR_ROLE_OWNER,
@ -1765,8 +1758,6 @@ class AddonUser(caching.CachingMixin, OnChangeMixin, SaveUpdateMixin,
listed = models.BooleanField(_(u'Listed'), default=True)
position = models.IntegerField(default=0)
objects = caching.CachingManager()
def __init__(self, *args, **kwargs):
super(AddonUser, self).__init__(*args, **kwargs)
self._original_role = self.role

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

@ -79,7 +79,7 @@ def update_last_updated(addon_id):
def update_appsupport(ids):
log.info("[%s@None] Updating appsupport for %s." % (len(ids), ids))
addons = Addon.objects.no_cache().filter(id__in=ids).no_transforms()
addons = Addon.objects.filter(id__in=ids).no_transforms()
support = []
for addon in addons:
for app, appver in addon.compatible_apps.items():
@ -99,9 +99,6 @@ def update_appsupport(ids):
AppSupport.objects.filter(addon__id__in=ids).delete()
AppSupport.objects.bulk_create(support)
# All our updates were sql, so invalidate manually.
Addon.objects.invalidate(*addons)
@task
def delete_preview_files(id, **kw):

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

@ -1,6 +1,5 @@
{% from "addons/listing/macros.html" import heading, item_info %}
{% set collection = collection or None %}
{% set collection_cache_key = collection.cache_key if collection else 'collection-none' %}
{% set userpk = request.user.pk if request.user.is_authenticated() else '' %}
{% for addon in addons %}
<div class="item">

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

@ -159,7 +159,7 @@ class TestTagsForm(TestCase):
return form
def get_tag_text(self):
return [t.tag_text for t in self.addon.tags.no_cache().all()]
return [t.tag_text for t in self.addon.tags.all()]
def test_tags(self):
self.add_tags('foo, bar')

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

@ -155,7 +155,7 @@ class TestAddonIndexer(TestCase):
if 'index' in prop)
def _extract(self):
qs = Addon.unfiltered.filter(id__in=[self.addon.pk]).no_cache()
qs = Addon.unfiltered.filter(id__in=[self.addon.pk])
for t in self.transforms:
qs = qs.transform(t)
self.addon = list(qs)[0]

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

@ -1768,10 +1768,10 @@ class TestUpdateStatus(TestCase):
addon = Addon.objects.create(type=amo.ADDON_EXTENSION)
addon.status = amo.STATUS_NOMINATED
addon.save()
assert Addon.objects.no_cache().get(pk=addon.pk).status == (
assert Addon.objects.get(pk=addon.pk).status == (
amo.STATUS_NOMINATED)
Version.objects.create(addon=addon)
assert Addon.objects.no_cache().get(pk=addon.pk).status == (
assert Addon.objects.get(pk=addon.pk).status == (
amo.STATUS_NULL)
def test_no_valid_file_ends_with_NULL(self):
@ -1781,22 +1781,22 @@ class TestUpdateStatus(TestCase):
version=version)
addon.status = amo.STATUS_NOMINATED
addon.save()
assert Addon.objects.no_cache().get(pk=addon.pk).status == (
assert Addon.objects.get(pk=addon.pk).status == (
amo.STATUS_NOMINATED)
f.status = amo.STATUS_DISABLED
f.save()
assert Addon.objects.no_cache().get(pk=addon.pk).status == (
assert Addon.objects.get(pk=addon.pk).status == (
amo.STATUS_NULL)
def test_unlisted_versions_ignored(self):
addon = addon_factory(status=amo.STATUS_PUBLIC)
addon.update_status()
assert Addon.objects.no_cache().get(pk=addon.pk).status == (
assert Addon.objects.get(pk=addon.pk).status == (
amo.STATUS_PUBLIC)
addon.current_version.update(channel=amo.RELEASE_CHANNEL_UNLISTED)
# update_status will have been called via versions.models.update_status
assert Addon.objects.no_cache().get(pk=addon.pk).status == (
assert Addon.objects.get(pk=addon.pk).status == (
amo.STATUS_NULL) # No listed versions so now NULL

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

@ -12,11 +12,11 @@ from django.utils.translation import ugettext
import olympia.core.logger
from olympia import amo
from olympia.lib.cache import memoize, memoize_key
from olympia.amo.utils import normalize_string
from olympia.constants.categories import CATEGORIES_BY_ID
from olympia.discovery.utils import call_recommendation_server
from olympia.translations.fields import LocaleList, LocaleValidationError
from olympia.lib.cache import memoize, memoize_key
log = olympia.core.logger.getLogger('z.redis')
@ -31,7 +31,7 @@ def clear_get_featured_ids_cache(*args, **kwargs):
cache.delete(cache_key)
@memoize('addons:featured', time=60 * 10)
@memoize('addons:featured', timeout=60 * 10)
def get_featured_ids(app=None, lang=None, type=None, types=None):
from olympia.addons.models import Addon
ids = []
@ -65,7 +65,7 @@ def get_featured_ids(app=None, lang=None, type=None, types=None):
return map(int, ids)
@memoize('addons:creatured', time=60 * 10)
@memoize('addons:creatured', timeout=60 * 10)
def get_creatured_ids(category, lang=None):
from olympia.addons.models import Addon
from olympia.bandwagon.models import FeaturedCollection

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

@ -47,7 +47,7 @@ from olympia.search.filters import (
SortingFilter)
from olympia.translations.query import order_by_translation
from olympia.versions.models import Version
from olympia.lib.cache import cache_get_or_set, make_key
from olympia.lib.cache import make_key, cache_get_or_set
from .decorators import addon_view_factory
from .indexers import AddonIndexer
@ -144,8 +144,7 @@ def extension_detail(request, addon):
def _category_personas(qs, limit):
def fetch_personas():
return randslice(qs, limit=limit)
# TODO: .query_key comes from cache-machine, find replacement
key = make_key('cat-personas:' + qs.query_key(), normalize=True)
key = make_key('cat-personas:' + str(qs.query), normalize=True)
return cache_get_or_set(key, fetch_personas)
@ -453,9 +452,7 @@ class AddonViewSet(RetrieveModelMixin, GenericViewSet):
lookup_value_regex = '[^/]+' # Allow '.' for email-like guids.
def get_queryset(self):
"""Return queryset to be used for the view. We implement our own that
does not depend on self.queryset to avoid cache-machine caching the
queryset too agressively (mozilla/addons-frontend#2497)."""
"""Return queryset to be used for the view."""
# Special case: admins - and only admins - can see deleted add-ons.
# This is handled outside a permission class because that condition
# would pollute all other classes otherwise.
@ -907,7 +904,6 @@ class LanguageToolsView(ListAPIView):
"""
return (
Addon.objects.public()
.no_cache()
.filter(appsupport__app=application, type__in=addon_types,
target_locale__isnull=False)
.exclude(target_locale='')
@ -939,7 +935,7 @@ class LanguageToolsView(ListAPIView):
# can avoid loading translations by removing transforms and then
# re-applying the default one that takes care of the files and compat
# info.
versions_qs = Version.objects.no_cache().filter(
versions_qs = Version.objects.filter(
apps__application=application,
apps__min__version_int__lte=appversions['min'],
apps__max__version_int__gte=appversions['max'],

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

@ -102,24 +102,6 @@ json_view.error = lambda s: http.HttpResponseBadRequest(
json.dumps(s), content_type='application/json')
class skip_cache(object):
def __init__(self, f):
functools.update_wrapper(self, f)
# Do this after `update_wrapper`, or it may be overwritten
# by a value from `f.__dict__`.
self.f = f
def __call__(self, *args, **kw):
with context.skip_cache():
return self.f(*args, **kw)
def __repr__(self):
return '<SkipCache %r>' % self.f
def __get__(self, obj, typ=None):
return skip_cache(self.f.__get__(obj, typ))
def use_master(f):
@functools.wraps(f)
def wrapper(*args, **kw):
@ -129,7 +111,7 @@ def use_master(f):
def write(f):
return use_master(skip_cache(f))
return use_master(f)
def set_modified_on(f):

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

@ -225,6 +225,7 @@ class SetRemoteAddrFromForwardedFor(object):
known = getattr(settings, 'KNOWN_PROXIES', [])
ips.reverse()
for ip in ips:
request.META['REMOTE_ADDR'] = ip
if ip not in known:

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

@ -1,20 +1,15 @@
import contextlib
import os
import threading
import time
from django.conf import settings
from django.core.files.storage import default_storage as storage
from django.db import models, transaction
from django.utils import translation
from django.utils.encoding import force_text
import caching.base
import elasticsearch
import multidb.pinning
from django_statsd.clients import statsd
import olympia.core.logger
import olympia.lib.queryset_transform as queryset_transform
@ -23,12 +18,6 @@ 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 = not settings.CACHE_MACHINE_ENABLED
log = olympia.core.logger.getLogger('z.addons')
@ -43,18 +32,7 @@ def use_master():
multidb.pinning._locals.pinned = old
@contextlib.contextmanager
def skip_cache():
"""Within this context, no queries come from cache."""
old = getattr(_locals, 'skip_cache', not settings.CACHE_MACHINE_ENABLED)
_locals.skip_cache = True
try:
yield
finally:
_locals.skip_cache = old
class TransformQuerySet(queryset_transform.TransformQuerySet):
class BaseQuerySet(queryset_transform.TransformQuerySet):
def pop_transforms(self):
qs = self._clone()
@ -72,11 +50,6 @@ class TransformQuerySet(queryset_transform.TransformQuerySet):
return (self.no_transforms().extra(select={'_only_trans': 1})
.transform(transformer.get_trans))
def transform(self, fn):
from . import decorators
f = decorators.skip_cache(fn)
return super(TransformQuerySet, self).transform(f)
class RawQuerySet(models.query.RawQuerySet):
"""A RawQuerySet with __len__."""
@ -94,20 +67,17 @@ class RawQuerySet(models.query.RawQuerySet):
return len(list(self.__iter__()))
class CachingRawQuerySet(RawQuerySet, caching.base.CachingRawQuerySet):
"""A RawQuerySet with __len__ and caching."""
# Make TransformQuerySet one of CachingQuerySet's parents so that we can do
# transforms on objects and then get them cached.
CachingQuerySet = caching.base.CachingQuerySet
CachingQuerySet.__bases__ = (TransformQuerySet,) + CachingQuerySet.__bases__
class UncachedManagerBase(models.Manager):
class ManagerBase(models.Manager):
# This needs to be set so that this manager class is being used
# for related objects too. This helps resolving translation fields
# on related fields.
# This also ensures we don't ignore soft-deleted items when traversing
# relations, if they are hidden by the objects manager, like we
# do with `addons.models:Addon`.
use_for_related_fields = True
def get_queryset(self):
qs = self._with_translations(TransformQuerySet(self.model))
qs = self._with_translations(BaseQuerySet(self.model))
return qs
def _with_translations(self, qs):
@ -144,37 +114,6 @@ class UncachedManagerBase(models.Manager):
return self.create(**kw), True
class ManagerBase(caching.base.CachingManager, UncachedManagerBase):
"""
Base for all managers in AMO.
Returns TransformQuerySets from the queryset_transform project.
If a model has translated fields, they'll be attached through a transform
function.
"""
def get_queryset(self):
qs = super(ManagerBase, self).get_queryset()
if getattr(_locals, 'skip_cache', False):
qs = qs.no_cache()
return self._with_translations(qs)
def raw(self, raw_query, params=None, *args, **kwargs):
return CachingRawQuerySet(raw_query, self.model, params=params,
using=self._db, *args, **kwargs)
def post_save(self, *args, **kwargs):
# Measure cache invalidation after saving an object.
with statsd.timer('cache_machine.manager.post_save'):
return super(ManagerBase, self).post_save(*args, **kwargs)
def post_delete(self, *args, **kwargs):
# Measure cache invalidation after deleting an object.
with statsd.timer('cache_machine.manager.post_delete'):
return super(ManagerBase, self).post_delete(*args, **kwargs)
class _NoChangeInstance(object):
"""A proxy for object instances to make safe operations within an
OnChangeMixin.on_change() callback.
@ -389,10 +328,9 @@ class SaveUpdateMixin(object):
return super(SaveUpdateMixin, self).save(**kwargs)
class UncachedModelBase(SearchMixin, SaveUpdateMixin, models.Model):
class ModelBase(SearchMixin, SaveUpdateMixin, models.Model):
"""
Base class for AMO models to abstract some common features
(without cache-machine).
Base class for AMO models to abstract some common features.
* Adds automatic created and modified fields to the model.
* Fetches all translations in one subsequent query during initialization.
@ -401,7 +339,7 @@ class UncachedModelBase(SearchMixin, SaveUpdateMixin, models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
objects = UncachedManagerBase()
objects = ManagerBase()
class Meta:
abstract = True
@ -417,29 +355,6 @@ class UncachedModelBase(SearchMixin, SaveUpdateMixin, models.Model):
return self._meta.app_label, self._meta.model_name, self.pk
class ModelBase(caching.base.CachingMixin, UncachedModelBase, models.Model):
"""
Base class for AMO models to abstract some common features.
* Adds automatic created and modified fields to the model.
* Fetches all translations in one subsequent query during initialization.
"""
objects = ManagerBase()
class Meta(UncachedModelBase.Meta):
abstract = True
@classmethod
def _cache_key(cls, pk, db):
"""
Custom django-cache-machine cache key implementation that avoids having
the real db in the key, since we are only using master-slaves we don't
need it and it avoids invalidation bugs with FETCH_BY_ID.
"""
key_parts = ('o', cls._meta, pk, 'default')
return ':'.join(map(force_text, key_parts))
def manual_order(qs, pks, pk_name='id'):
"""
Given a query set and a list of primary keys, return a set of objects from

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

@ -1,7 +1,5 @@
import mock
import pytest
from django.conf import settings
from django.core.files.storage import default_storage as storage
from mock import Mock
@ -29,24 +27,6 @@ class ManualOrderTest(TestCase):
assert semi_arbitrary_order == [addon.id for addon in addons]
def test_skip_cache():
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
assert not getattr(local, 'pinned', False)
@ -162,24 +142,6 @@ class TestModelBase(TestCase):
Addon.get_unfiltered_manager() == Addon.unfiltered
UserProfile.get_unfiltered_manager() == UserProfile.objects
def test_measure_save_time(self):
addon = Addon.objects.create(type=amo.ADDON_EXTENSION)
with mock.patch('olympia.amo.models.statsd.timer') as timer:
addon.save()
timer.assert_any_call('cache_machine.manager.post_save')
def test_measure_delete_time(self):
addon = Addon.objects.create(type=amo.ADDON_EXTENSION)
with mock.patch('olympia.amo.models.statsd.timer') as timer:
addon.delete()
timer.assert_any_call('cache_machine.manager.post_delete')
def test_cache_key():
# Test that we are not taking the db into account when building our
# cache keys for django-cache-machine. See bug 928881.
assert Addon._cache_key(1, 'default') == Addon._cache_key(1, 'slave')
class BasePreviewMixin(object):

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

@ -87,9 +87,9 @@ class TestCommands(TestCase):
'.PyQuery', spec=True)
def test_import_prod_versions(self, pyquery_mock):
assert not AppVersion.objects.filter(
application=amo.FIREFOX.id, version='53.0').no_cache().exists()
application=amo.FIREFOX.id, version='53.0').exists()
assert not AppVersion.objects.filter(
application=amo.FIREFOX.id, version='53.*').no_cache().exists()
application=amo.FIREFOX.id, version='53.*').exists()
# Result of PyQuery()
MockedDoc = mock.Mock()
@ -108,6 +108,6 @@ class TestCommands(TestCase):
call_command('import_prod_versions')
assert AppVersion.objects.filter(
application=amo.FIREFOX.id, version='53.0').no_cache().exists()
application=amo.FIREFOX.id, version='53.0').exists()
assert AppVersion.objects.filter(
application=amo.FIREFOX.id, version='53.*').no_cache().exists()
application=amo.FIREFOX.id, version='53.*').exists()

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

@ -13,7 +13,7 @@ from olympia import activity, amo
from olympia.access import acl
from olympia.addons.models import Addon
from olympia.addons.utils import clear_get_featured_ids_cache
from olympia.amo.models import UncachedManagerBase, UncachedModelBase
from olympia.amo.models import ManagerBase, ModelBase
from olympia.amo.templatetags.jinja_helpers import (
absolutify, user_media_path, user_media_url)
from olympia.amo.urlresolvers import reverse
@ -60,7 +60,7 @@ class CollectionQuerySet(queryset_transform.TransformQuerySet):
select_params=(addon_id,))
class CollectionManager(UncachedManagerBase):
class CollectionManager(ManagerBase):
def get_queryset(self):
qs = super(CollectionManager, self).get_queryset()
@ -84,7 +84,7 @@ class CollectionManager(UncachedManagerBase):
return self.filter(author=user.pk)
class Collection(UncachedModelBase):
class Collection(ModelBase):
TYPE_CHOICES = amo.COLLECTION_CHOICES.items()
# TODO: Use models.UUIDField but it uses max_length=32 hex (no hyphen)
@ -132,7 +132,7 @@ class Collection(UncachedModelBase):
top_tags = TopTags()
class Meta(UncachedModelBase.Meta):
class Meta(ModelBase.Meta):
db_table = 'collections'
unique_together = (('author', 'slug'),)
@ -379,7 +379,7 @@ models.signals.post_delete.connect(Collection.post_delete, sender=Collection,
dispatch_uid='coll.post_delete')
class CollectionAddon(UncachedModelBase):
class CollectionAddon(ModelBase):
addon = models.ForeignKey(Addon)
collection = models.ForeignKey(Collection)
# category (deprecated: for "Fashion Your Firefox")
@ -392,7 +392,7 @@ class CollectionAddon(UncachedModelBase):
help_text='Add-ons are displayed in ascending order '
'based on this field.')
class Meta(UncachedModelBase.Meta):
class Meta(ModelBase.Meta):
db_table = 'addons_collections'
unique_together = (('addon', 'collection'),)
@ -428,11 +428,11 @@ models.signals.post_delete.connect(CollectionAddon.post_delete,
dispatch_uid='coll.post_delete')
class CollectionWatcher(UncachedModelBase):
class CollectionWatcher(ModelBase):
collection = models.ForeignKey(Collection, related_name='following')
user = models.ForeignKey(UserProfile)
class Meta(UncachedModelBase.Meta):
class Meta(ModelBase.Meta):
db_table = 'collection_subscriptions'
@staticmethod
@ -472,7 +472,7 @@ models.signals.post_delete.connect(CollectionVote.post_save_or_delete,
sender=CollectionVote)
class FeaturedCollection(UncachedModelBase):
class FeaturedCollection(ModelBase):
application = models.PositiveIntegerField(choices=amo.APPS_CHOICES,
db_column='application_id')
collection = models.ForeignKey(Collection)
@ -497,7 +497,7 @@ models.signals.post_delete.connect(FeaturedCollection.post_save_or_delete,
sender=FeaturedCollection)
class MonthlyPick(UncachedModelBase):
class MonthlyPick(ModelBase):
addon = models.ForeignKey(Addon)
blurb = models.TextField()
image = models.URLField()

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

@ -83,10 +83,9 @@ class ThisCollectionDefault(object):
class CollectionAddonSerializer(serializers.ModelSerializer):
addon = SplitField(
SlugOrPrimaryKeyRelatedField(
# .no_cache() because django-cache-machine blows up otherwise.
# Only used for writes (this is input field) so no perf concerns.
queryset=Addon.objects.public().no_cache()),
# Only used for writes (this is input field), so there are no perf
# concerns and we don't use any special caching.
SlugOrPrimaryKeyRelatedField(queryset=Addon.objects.public()),
AddonSerializer())
notes = TranslationSerializerField(source='comments', required=False)
collection = serializers.HiddenField(default=ThisCollectionDefault())

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

@ -930,7 +930,7 @@ class TestWatching(TestCase):
class TestCollectionForm(TestCase):
fixtures = ['base/collection_57181', 'users/test_backends']
@patch('olympia.amo.models.UncachedModelBase.update')
@patch('olympia.amo.models.ModelBase.update')
def test_icon(self, update_mock):
collection = Collection.objects.get(pk=57181)
# TODO(andym): altering this form is too complicated, can we simplify?

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

@ -79,9 +79,6 @@ 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',
@ -119,8 +116,6 @@ REDIS_BACKENDS = {
'slave': get_redis_settings(env('REDIS_BACKENDS_SLAVE'))
}
CACHE_MACHINE_USE_REDIS = True
csp = 'csp.middleware.CSPMiddleware'
ES_TIMEOUT = 60

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

@ -104,8 +104,6 @@ REDIS_BACKENDS = {
'slave': get_redis_settings(env('REDIS_BACKENDS_SLAVE'))
}
CACHE_MACHINE_USE_REDIS = True
ES_TIMEOUT = 60
ES_HOSTS = env('ES_HOSTS')
ES_URLS = ['http://%s' % h for h in ES_HOSTS]

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

@ -78,9 +78,6 @@ 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',
@ -115,8 +112,6 @@ REDIS_BACKENDS = {
'slave': get_redis_settings(env('REDIS_BACKENDS_SLAVE'))
}
CACHE_MACHINE_USE_REDIS = True
csp = 'csp.middleware.CSPMiddleware'
ES_TIMEOUT = 60

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

@ -36,7 +36,7 @@ def change():
k = 0
print('Changing %s files' % len(files))
for chunk in chunked(files, 100):
for file in File.objects.no_cache().filter(pk__in=chunk):
for file in File.objects.filter(pk__in=chunk):
file.platform = amo.PLATFORM_ALL.id
if not file.datestatuschanged:
file.datestatuschanged = datetime.now()
@ -52,7 +52,7 @@ def report():
.filter(addon__type=amo.ADDON_SEARCH)
.filter(files_count__gt=1))
for chunk in chunked(versions, 100):
for version in Version.objects.no_cache().filter(pk__in=chunk):
for version in Version.objects.filter(pk__in=chunk):
print('Addon: %s, %s' % (version.addon.pk,
version.addon.name))
print('Version: %s - %s files' % (version.pk,

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

@ -61,7 +61,7 @@ class BaseTestEdit(TestCase):
self.url = self.addon.get_dev_url()
def get_addon(self):
return Addon.objects.no_cache().get(id=3615)
return Addon.objects.get(id=3615)
def get_url(self, section, edit=False):
args = [self.addon.slug, section]

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

@ -27,10 +27,10 @@ class TestOwnership(TestCase):
return formset(*args, **defaults)
def get_version(self):
return Version.objects.no_cache().get(id=self.version.id)
return Version.objects.get(id=self.version.id)
def get_addon(self):
return Addon.objects.no_cache().get(id=self.addon.id)
return Addon.objects.get(id=self.addon.id)
class TestEditPolicy(TestOwnership):
@ -246,7 +246,7 @@ class TestEditAuthor(TestOwnership):
assert ActivityLog.objects.all().count() == orig
def test_success_add_user(self):
qs = (AddonUser.objects.no_cache().filter(addon=3615)
qs = (AddonUser.objects.filter(addon=3615)
.values_list('user', flat=True))
assert list(qs.all()) == [55021]
@ -296,8 +296,7 @@ class TestEditAuthor(TestOwnership):
data = self.formset(one.initial, two.initial, empty, initial_count=2)
response = self.client.post(self.url, data)
self.assert3xx(response, self.url, 302)
assert not AddonUser.objects.no_cache().get(
addon=3615, user=999).listed
assert not AddonUser.objects.get(addon=3615, user=999).listed
def test_change_user_role(self):
# Add an author b/c we can't edit anything about the current one.

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

@ -83,7 +83,7 @@ class TestSubmitBase(TestCase):
self.addon = self.get_addon()
def get_addon(self):
return Addon.objects.no_cache().get(pk=3615)
return Addon.objects.get(pk=3615)
def get_version(self):
return self.get_addon().versions.latest()

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

@ -571,7 +571,7 @@ class TestVersion(TestCase):
class TestVersionEditMixin(object):
def get_addon(self):
return Addon.objects.no_cache().get(id=3615)
return Addon.objects.get(id=3615)
def get_version(self):
return self.get_addon().current_version
@ -798,7 +798,7 @@ class TestVersionEditSearchEngine(TestVersionEditMixin, TestCase):
response = self.client.post(self.url, dd)
assert response.status_code == 302
version = Addon.objects.no_cache().get(id=4594).current_version
version = Addon.objects.get(id=4594).current_version
assert unicode(version.releasenotes) == 'xx'
assert unicode(version.approvalnotes) == 'yy'
@ -893,7 +893,7 @@ class TestVersionEditFiles(TestVersionEditBase):
forms = self.client.get(self.url).context['file_form'].forms
forms = map(initial, forms)
self.client.post(self.url, self.formset(*forms, prefix='files'))
assert File.objects.no_cache().get(pk=bsd.pk).platform == (
assert File.objects.get(pk=bsd.pk).platform == (
amo.PLATFORM_BSD.id)
def test_all_unsupported_platforms_change(self):
@ -903,7 +903,7 @@ class TestVersionEditFiles(TestVersionEditBase):
# Update the file platform to Linux:
forms[1]['platform'] = amo.PLATFORM_LINUX.id
self.client.post(self.url, self.formset(*forms, prefix='files'))
assert File.objects.no_cache().get(pk=bsd.pk).platform == (
assert File.objects.get(pk=bsd.pk).platform == (
amo.PLATFORM_LINUX.id)
forms = self.client.get(self.url).context['file_form'].forms
choices = self.get_platforms(forms[1])
@ -945,7 +945,7 @@ class TestPlatformSearchEngine(TestVersionEditMixin, TestCase):
approvalnotes='yy')
response = self.client.post(self.url, dd)
assert response.status_code == 302
file_ = Version.objects.no_cache().get(id=42352).files.all()[0]
file_ = Version.objects.get(id=42352).files.all()[0]
assert amo.PLATFORM_ALL.id == file_.platform

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

@ -1062,9 +1062,7 @@ def version_edit(request, addon_id, addon, version_id):
amo.permissions.REVIEWS_ADMIN)
if addon.accepts_compatible_apps():
# We should be in no-caching land but this one stays cached for some
# reason.
qs = version.apps.all().no_cache()
qs = version.apps.all()
compat_form = forms.CompatFormSet(
request.POST or None, queryset=qs,
form_kwargs={'version': version})

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

@ -35,7 +35,6 @@ def cleanup_extracted_file():
def cleanup_validation_results():
"""Will remove all validation results. Used when the validator is
upgraded and results may no longer be relevant."""
# With a large enough number of objects not using no_cache() tracebacks
all = FileValidation.objects.no_cache().all()
all = FileValidation.objects.all()
log.info('Removing %s old validation results.' % (all.count()))
all.delete()

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

@ -28,7 +28,7 @@ import olympia.core.logger
from olympia import amo
from olympia.lib.cache import memoize
from olympia.amo.decorators import use_master
from olympia.amo.models import ModelBase, OnChangeMixin, UncachedManagerBase
from olympia.amo.models import ModelBase, OnChangeMixin, ManagerBase
from olympia.amo.storage_utils import copy_stored_file, move_stored_file
from olympia.amo.templatetags.jinja_helpers import (
absolutify, urlparams, user_media_path, user_media_url)
@ -342,7 +342,7 @@ class File(OnChangeMixin, ModelBase):
_get_localepicker = re.compile('^locale browser ([\w\-_]+) (.*)$', re.M)
@memoize(prefix='localepicker', time=None)
@memoize(prefix='localepicker', timeout=None)
def get_localepicker(self):
"""
For a file that is part of a language pack, extract
@ -601,7 +601,7 @@ class FileUpload(ModelBase):
version = models.CharField(max_length=255, null=True)
addon = models.ForeignKey('addons.Addon', null=True)
objects = UncachedManagerBase()
objects = ManagerBase()
class Meta(ModelBase.Meta):
db_table = 'file_uploads'

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

@ -47,7 +47,7 @@ class TestWebextExtractPermissions(UploadTest):
call_command('extract_permissions')
file_ = File.objects.no_cache().get(id=file_.id)
file_ = File.objects.get(id=file_.id)
assert WebextPermission.objects.get(file=file_)
permissions_list = file_.webext_permissions_list
assert len(permissions_list) == 8
@ -74,7 +74,7 @@ class TestWebextExtractPermissions(UploadTest):
call_command('extract_permissions', force=True)
file_ = File.objects.no_cache().get(id=file_.id)
file_ = File.objects.get(id=file_.id)
assert WebextPermission.objects.get(file=file_)
assert len(file_.webext_permissions_list) == 8
@ -135,7 +135,7 @@ class TestWebextUpdateDescriptions(TestCase):
def _check_locales(self):
with translation.override('fr'):
assert WebextPermissionDescription.objects.no_cache().get(
assert WebextPermissionDescription.objects.get(
name='bookmarks').description == u'Réad n wríte le bookmarks'
# There wasn't any French l10n for this perm; so en fallback.
assert WebextPermissionDescription.objects.get(
@ -144,7 +144,7 @@ class TestWebextUpdateDescriptions(TestCase):
name='tabs').description == u'Accéder browser onglets'
with translation.override('de'):
assert WebextPermissionDescription.objects.no_cache().get(
assert WebextPermissionDescription.objects.get(
name='bookmarks').description == u'Eich bin bookmark'
# There wasn't any German l10n for this perm; so en fallback.
assert WebextPermissionDescription.objects.get(
@ -153,7 +153,7 @@ class TestWebextUpdateDescriptions(TestCase):
assert WebextPermissionDescription.objects.get(
name='tabs').description == u'Access browser tabs'
with translation.override('zh-CN'):
assert WebextPermissionDescription.objects.no_cache().get(
assert WebextPermissionDescription.objects.get(
name='bookmarks').description == u'讀取並修改書籤'
# There wasn't any Chinese l10n for this perm; so en fallback.
assert WebextPermissionDescription.objects.get(

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

@ -240,7 +240,7 @@ class TestFile(TestCase, amo.tests.AMOPaths):
def test_addon(self):
f = File.objects.get(pk=67442)
addon_id = f.version.addon_id
addon = Addon.objects.no_cache().get(pk=addon_id)
addon = Addon.objects.get(pk=addon_id)
addon.update(status=amo.STATUS_DELETED)
assert f.addon.id == addon_id

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

@ -8,13 +8,13 @@ from django.views.decorators.http import condition
import olympia.core.logger
from olympia.access import acl
from olympia.lib.cache import Message, Token
from olympia.amo.decorators import json_view
from olympia.amo.urlresolvers import reverse
from olympia.amo.utils import HttpResponseSendFile, render, urlparams
from olympia.files.decorators import (
compare_file_view, etag, file_view, file_view_token, last_modified)
from olympia.files.file_viewer import extract_file
from olympia.lib.cache import Message, Token
from . import forms

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

@ -94,7 +94,6 @@
{% endfor -%}
{%- endif -%}
{%- if new_api %}
{%- if addon.contributions -%}
<contribution_data>
<meet_developers>

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

@ -19,8 +19,6 @@ from django.utils.translation import get_language, ugettext, ugettext_lazy as _
import waffle
from caching.base import cached_with
import olympia.core.logger
from olympia import amo, legacy_api
@ -301,7 +299,6 @@ def guid_search(request, api_version, guids):
try:
# Only search through public (and not disabled) add-ons.
addon = Addon.objects.public().get(guid=guid)
except Addon.DoesNotExist:
addons_xml[key] = ''
@ -388,16 +385,17 @@ class SearchView(APIView):
.filter_query_string(query)
[:limit])
results = []
total = qs.count()
results = []
for addon in qs:
compat_version = find_compatible_version(
addon, app_id, params['version'], params['platform'],
compat_mode)
# Specific case for Personas (bug 990768): if we search providing
# the Persona addon type (9), then don't look for a compatible
# version.
# Specific case for Personas (bug 990768): if we search
# providing the Persona addon type (9), then don't look for a
# compatible version.
if compat_version or addon_type == '9':
addon.compat_version = compat_version
results.append(addon)
@ -439,8 +437,8 @@ class ListView(APIView):
limit=10, platform='ALL', version=None,
compat_mode='strict'):
"""
Find a list of new or featured add-ons. Filtering is done in Python
for cache-friendliness and to avoid heavy queries.
Find a list of new or featured add-ons. Filtering is done in Python
to avoid heavy queries.
"""
limit = min(MAX_LIMIT, int(limit))
APP, platform = self.request.APP, platform.lower()
@ -471,14 +469,8 @@ class ListView(APIView):
args = (addon_type, limit, APP, platform, version, compat_mode,
shuffle)
def f():
return self._process(addons, *args)
return cached_with(addons, f, map(force_bytes, args))
def _process(self, addons, *args):
return self.render('legacy_api/list.xml',
{'addons': addon_filter(addons, *args)})
{'addons': addon_filter(addons.all(), *args)})
def render_json(self, context):
return json.dumps([addon_to_dict(a) for a in context['addons']],

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

@ -30,7 +30,7 @@ class TestModuleAdmin(TestCase):
modules = qs.values_list('module', flat=True)
assert set(modules) == set(registry.keys())
qs = DiscoveryModule.objects.no_cache().filter(app=1)
qs = DiscoveryModule.objects.filter(app=1)
assert qs.count() == 0
# All our modules get added.

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

@ -90,7 +90,6 @@ def pane_promos(request, version, platform, compat_mode=None):
@non_atomic_requests
def pane_more_addons(request, section, version, platform, compat_mode=None):
if not compat_mode:
compat_mode = get_compat_mode(version)
@ -103,7 +102,9 @@ def pane_more_addons(request, section, version, platform, compat_mode=None):
ctx = {'featured_addons': from_api('featured')}
elif section == 'up-and-coming':
ctx = {'up_and_coming': from_api('hotness')}
return render(request, 'legacy_discovery/more_addons.html', ctx)
content = render(request, 'legacy_discovery/more_addons.html', ctx)
return content
def get_modules(request, platform, version):
@ -145,10 +146,9 @@ def api_view(request, platform, version, list_type, api_version=1.5,
def module_admin(request):
APP = request.APP
# Custom sorting to drop ordering=NULL objects to the bottom.
with amo.models.skip_cache():
qs = DiscoveryModule.objects.raw("""
SELECT * from discovery_modules WHERE app_id = %s
ORDER BY ordering IS NULL, ordering""", [APP.id])
qs = DiscoveryModule.objects.raw("""
SELECT * from discovery_modules WHERE app_id = %s
ORDER BY ordering IS NULL, ordering""", [APP.id])
qs.ordered = True # The formset looks for this.
_sync_db_and_registry(qs, APP.id)

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

@ -5,9 +5,8 @@ import re
import uuid
from contextlib import contextmanager
from django.core.cache import cache, _create_cache
from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache
from django.core import cache as django_cache
from django.core.cache import cache, caches, _create_cache
from django.utils import encoding, translation
from django.conf import settings
@ -32,7 +31,7 @@ def cache_get_or_set(key, default, timeout=DEFAULT_TIMEOUT, version=None):
Return the value of the key stored or retrieved.
Backport from Django 1.11
Backport from Django 1.11.
"""
val = cache.get(key, version=version)
@ -68,43 +67,24 @@ def memoize_key(prefix, *args, **kwargs):
prefix, key.hexdigest())
def memoize_get(prefix, *args, **kwargs):
"""
Returns the content of the cache.
:param prefix: a prefix for the key in memcache
:type prefix: string
:param args: arguments to be str()'d to form the key
:type args: list
:param kwargs: arguments to be str()'d to form the key
:type kwargs: list
"""
return cache.get(memoize_key(prefix, *args, **kwargs))
def memoize(prefix, time=60):
def memoize(prefix, timeout=60):
"""
A simple decorator that caches into memcache, using a simple
key based on stringing args and kwargs.
Arguments to the method must be easily and consistently serializable
using str(..) otherwise the cache key will be inconsistent.
:param prefix: a prefix for the key in memcache
:type prefix: string
:param time: number of seconds to cache the key for, default 60 seconds
:type time: integer
:param timeout: number of seconds to cache the key for, default 60 seconds
:type timeout: integer
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
def wrapped_func():
return func(*args, **kwargs)
key = memoize_key(prefix, *args, **kwargs)
data = cache.get(key)
if data is not None:
return data
data = func(*args, **kwargs)
cache.set(key, data, time)
return data
return cache_get_or_set(key, wrapped_func, timeout=timeout)
return wrapper
return decorator
@ -222,8 +202,8 @@ class CacheStatTracker(BaseCache):
@contextmanager
def assert_cache_requests(num):
cache_using = django_cache.caches['default']
def assert_cache_requests(num, alias='default'):
cache_using = caches[alias]
cache_using.clear_log()
yield
@ -231,5 +211,4 @@ def assert_cache_requests(num):
executed = len(cache_using.requests_log)
assert executed == num, "%d requests executed, %d expected" % (
executed, num,
)
executed, num)

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

@ -36,11 +36,7 @@ def index_objects(ids, model, extract_func, index=None, transforms=None,
if transforms is None:
transforms = []
if hasattr(objects, 'no_cache'):
qs = objects.no_cache()
else:
qs = objects
qs = qs.filter(id__in=ids)
qs = objects.filter(id__in=ids)
for t in transforms:
qs = qs.transform(t)

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

@ -992,10 +992,6 @@ 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

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

@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
from django.test import TestCase
from django.test.utils import override_settings
from django.utils import translation
from django.core.cache import cache
from unittest import TestCase
from olympia.lib.cache import (
cache_get_or_set, make_key, Message, Token, memoize, memoize_get,
memoize_key)
Message, Token, memoize, memoize_key, cache_get_or_set,
make_key)
@override_settings(KEY_PREFIX='amo:test:')
@ -61,7 +61,8 @@ def test_memoize():
def add(*args):
return sum(args)
assert add(1, 2) == memoize_get('f', 1, 2)
cache_key = memoize_key('f', 1, 2)
assert add(1, 2) == cache.get(cache_key)
class TestToken(TestCase):

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

@ -3,8 +3,6 @@ from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
import caching.base as caching
import olympia.core.logger
from olympia import activity, amo
@ -44,7 +42,7 @@ class WithoutRepliesRatingManager(ManagerBase):
return qs.filter(reply_to__isnull=True)
class RatingQuerySet(caching.CachingQuerySet):
class RatingQuerySet(models.QuerySet):
"""
A queryset modified for soft deletion.
"""

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

@ -183,7 +183,7 @@ class RatingSerializer(BaseRatingSerializer):
raise serializers.ValidationError(
ugettext(u'You can\'t leave a review on your own add-on.'))
rating_exists_on_this_version = Rating.objects.no_cache().filter(
rating_exists_on_this_version = Rating.objects.filter(
addon=data['addon'], user=data['user'],
version=data['version']).using('default').exists()
if rating_exists_on_this_version:

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

@ -5,7 +5,7 @@ import olympia.core.logger
from olympia.addons.models import Addon
from olympia.amo.celery import task
from olympia.amo.decorators import write
from olympia.lib.cache import cache_get_or_set, make_key
from olympia.lib.cache import cache_get_or_set
from .models import GroupedRating, Rating
@ -23,7 +23,7 @@ def update_denorm(*pairs, **kw):
log.info('[%s@%s] Updating review denorms.' %
(len(pairs), update_denorm.rate_limit))
for addon, user in pairs:
reviews = list(Rating.without_replies.all().no_cache()
reviews = list(Rating.without_replies.all()
.filter(addon=addon, user=user).order_by('created'))
if not reviews:
continue
@ -48,14 +48,14 @@ def addon_rating_aggregates(addons, **kw):
# The following returns something like
# [{'rating': 2.0, 'addon': 7L, 'count': 5},
# {'rating': 3.75, 'addon': 6L, 'count': 8}, ...]
qs = (Rating.without_replies.all().no_cache()
qs = (Rating.without_replies.all()
.filter(addon__in=addons, is_latest=True)
.values('addon') # Group by addon id.
.annotate(rating=Avg('rating'), count=Count('addon')) # Aggregates.
.order_by()) # Reset order by so that `created` is not included.
stats = {x['addon']: (x['rating'], x['count']) for x in qs}
text_qs = (Rating.without_replies.all().no_cache()
text_qs = (Rating.without_replies.all()
.filter(addon__in=addons, is_latest=True)
.exclude(body=None)
.values('addon') # Group by addon id.
@ -84,15 +84,14 @@ def addon_bayesian_rating(*addons, **kw):
log.info('[%s@%s] Updating bayesian ratings.' %
(len(addons), addon_bayesian_rating.rate_limit))
avg = cache_get_or_set(
make_key('task.bayes.avg'), addon_aggregates, 60 * 60 * 60)
avg = cache_get_or_set('task.bayes.avg', addon_aggregates, 60 * 60 * 60)
# Rating can be NULL in the DB, so don't update it if it's not there.
if avg['rating'] is None:
return
mc = avg['reviews'] * avg['rating']
for addon in Addon.objects.no_cache().filter(id__in=addons):
for addon in Addon.objects.filter(id__in=addons):
if addon.average_rating is None:
# Ignoring addons with no average rating.
continue

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

@ -1346,9 +1346,6 @@ class TestRatingViewSetGet(TestCase):
data = json.loads(response.content)
assert data == {'detail': 'version parameter should be an integer.'}
# settings_test sets CACHE_COUNT_TIMEOUT to -1 and it's too late to
# override it, so instead mock the TIMEOUT property in cache-machine.
@mock.patch('caching.config.TIMEOUT', 300)
def test_get_then_post_then_get_any_caching_is_cleared(self):
"""Make sure there is no overzealous caching going on when requesting
the list of reviews for a given user+addon+version combination.

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

@ -44,7 +44,7 @@ class Command(BaseCommand):
addon__status__in=(amo.STATUS_PUBLIC, amo.STATUS_NOMINATED),
files__status=amo.STATUS_AWAITING_REVIEW,
files__is_webextension=True)
.no_cache().order_by('nomination', 'created').distinct())
.order_by('nomination', 'created').distinct())
def handle(self, *args, **options):
"""Command entry point."""

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

@ -17,7 +17,7 @@ from olympia import amo
from olympia.abuse.models import AbuseReport
from olympia.access import acl
from olympia.addons.models import Addon, Persona
from olympia.amo.models import ManagerBase, ModelBase, skip_cache
from olympia.amo.models import ManagerBase, ModelBase
from olympia.amo.templatetags.jinja_helpers import absolutify
from olympia.amo.urlresolvers import reverse
from olympia.amo.utils import cache_ns_key, send_mail
@ -539,7 +539,7 @@ class ReviewerScore(ModelBase):
if val is not None:
return val
val = (ReviewerScore.objects.no_cache().filter(user=user)
val = (ReviewerScore.objects.filter(user=user)
.aggregate(total=Sum('score'))
.values())[0]
if val is None:
@ -556,7 +556,7 @@ class ReviewerScore(ModelBase):
if val is not None:
return val
val = ReviewerScore.objects.no_cache().filter(user=user)
val = ReviewerScore.objects.filter(user=user)
if addon_type is not None:
val.filter(addon__type=addon_type)
@ -582,8 +582,7 @@ class ReviewerScore(ModelBase):
GROUP BY `addons`.`addontype_id`
ORDER BY `total` DESC
"""
with skip_cache():
val = list(ReviewerScore.objects.raw(sql, [user.id]))
val = list(ReviewerScore.objects.raw(sql, [user.id]))
cache.set(key, val, None)
return val
@ -608,8 +607,7 @@ class ReviewerScore(ModelBase):
GROUP BY `addons`.`addontype_id`
ORDER BY `total` DESC
"""
with skip_cache():
val = list(ReviewerScore.objects.raw(sql, [user.id, since]))
val = list(ReviewerScore.objects.raw(sql, [user.id, since]))
cache.set(key, val, 3600)
return val

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

@ -2112,8 +2112,7 @@ class TestContentReviewQueue(QueueTest):
AutoApprovalSummary.objects.create(
version=addon4.current_version,
verdict=amo.AUTO_APPROVED, confirmed=True)
assert not AddonApprovalsCounter.objects.no_cache().filter(
addon=addon4).exists()
assert not AddonApprovalsCounter.objects.filter(addon=addon4).exists()
# Addons with no last_content_review date should be first, ordered by
# their creation date, older first.

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

@ -104,12 +104,12 @@ def _get_themes(request, reviewer, flagged=False, rereview=False):
num, themes, locks = _get_rereview_themes(reviewer)
else:
# Pending and flagged themes.
locks = ThemeLock.objects.no_cache().filter(
locks = ThemeLock.objects.filter(
reviewer=reviewer, theme__addon__status=status)
num, themes = _calc_num_themes_checkout(locks)
if themes:
return themes
themes = Persona.objects.no_cache().filter(
themes = Persona.objects.filter(
addon__status=status, themelock=None)
# Don't allow self-reviews.
@ -141,7 +141,7 @@ def _get_themes(request, reviewer, flagged=False, rereview=False):
locks = expired_locks
if rereview:
return (RereviewQueueTheme.objects.no_cache()
return (RereviewQueueTheme.objects
.filter(theme__themelock__reviewer=reviewer)
.exclude(theme__addon__status=amo.STATUS_REJECTED))
@ -247,7 +247,7 @@ def _calc_num_themes_checkout(locks):
def _get_rereview_themes(reviewer):
"""Check out re-uploaded themes."""
locks = (ThemeLock.objects.select_related().no_cache()
locks = (ThemeLock.objects.select_related()
.filter(reviewer=reviewer,
theme__rereviewqueuetheme__isnull=False)
.exclude(theme__addon__status=amo.STATUS_REJECTED))
@ -256,7 +256,7 @@ def _get_rereview_themes(reviewer):
if updated_locks:
locks = updated_locks
themes = (RereviewQueueTheme.objects.no_cache()
themes = (RereviewQueueTheme.objects
.filter(theme__addon__isnull=False, theme__themelock=None)
.exclude(theme__addon__status=amo.STATUS_REJECTED))
return num, themes, locks

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

@ -2,8 +2,6 @@
from django.db import models
from django_extensions.db.fields.json import JSONField
import caching.base
from olympia.amo.models import SearchMixin
@ -137,13 +135,11 @@ class ThemeUpdateCountBulk(models.Model):
db_table = 'theme_update_counts_bulk'
class GlobalStat(caching.base.CachingMixin, models.Model):
class GlobalStat(models.Model):
name = models.CharField(max_length=255)
count = models.IntegerField()
date = models.DateField()
objects = caching.base.CachingManager()
class Meta:
db_table = 'global_stats'
unique_together = ('name', 'date')

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

@ -21,10 +21,10 @@ class TestGlobalStats(TestCase):
date = datetime.date(2009, 6, 1)
job = 'addon_total_downloads'
assert GlobalStat.objects.no_cache().filter(
assert GlobalStat.objects.filter(
date=date, name=job).count() == 0
tasks.update_global_totals(job, date)
assert len(GlobalStat.objects.no_cache().filter(
assert len(GlobalStat.objects.filter(
date=date, name=job)) == 1
def test_count_stats_for_date(self):
@ -45,14 +45,14 @@ class TestGlobalStats(TestCase):
date = datetime.date.today()
job = 'addon_count_new'
tasks.update_global_totals(job, date)
global_stat = GlobalStat.objects.no_cache().get(date=date, name=job)
global_stat = GlobalStat.objects.get(date=date, name=job)
assert global_stat.count == 1
# Should still work if the date is passed as a datetime string (what
# celery serialization does).
job = 'version_count_new'
tasks.update_global_totals(job, datetime.datetime.now().isoformat())
global_stat = GlobalStat.objects.no_cache().get(date=date, name=job)
global_stat = GlobalStat.objects.get(date=date, name=job)
assert global_stat.count == 1
def test_through_cron(self):
@ -78,13 +78,11 @@ class TestGlobalStats(TestCase):
cron.update_global_totals()
job = 'addon_count_new'
global_stat = GlobalStat.objects.no_cache().get(
date=yesterday, name=job)
global_stat = GlobalStat.objects.get(date=yesterday, name=job)
assert global_stat.count == 1
job = 'version_count_new'
global_stat = GlobalStat.objects.no_cache().get(
date=yesterday, name=job)
global_stat = GlobalStat.objects.get(date=yesterday, name=job)
assert global_stat.count == 1
def test_input(self):

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

@ -444,7 +444,7 @@ _KEYS = {
_CACHED_KEYS = sorted(_KEYS.values())
@memoize(prefix='global_stats', time=60 * 60)
@memoize(prefix='global_stats', timeout=60 * 60)
def _site_query(period, start, end, field=None, request=None):
with connection.cursor() as cursor:
# Let MySQL make this fast. Make sure we prevent SQL injection with the

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

@ -1,6 +1,5 @@
from django.db import connections, models, router
from django.db.models.deletion import Collector
from django.utils.encoding import force_text
import bleach
@ -105,16 +104,6 @@ class Translation(ModelBase):
delete.alters_data = True
@classmethod
def _cache_key(cls, pk, db):
# Hard-coding the class name here so that subclasses don't try to cache
# themselves under something like "o:translations.purifiedtranslation".
#
# Like in ModelBase, we avoid putting the real db in the key because it
# does us more harm than good.
key_parts = ('o', 'translations.translation', pk, 'default')
return ':'.join(map(force_text, key_parts))
@classmethod
def new(cls, string, locale, id=None):
"""

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

@ -676,17 +676,3 @@ def test_comparison_with_lazy():
lazy_u = lazy(lambda x: x, unicode)
x == lazy_u('xxx')
lazy_u('xxx') == x
def test_cache_key():
# Test that we are not taking the db into account when building our
# cache keys for django-cache-machine. See bug 928881.
assert Translation._cache_key(1, 'default') == (
Translation._cache_key(1, 'slave'))
# Test that we are using the same cache no matter what Translation class
# we use.
assert PurifiedTranslation._cache_key(1, 'default') == (
Translation._cache_key(1, 'default'))
assert LinkifiedTranslation._cache_key(1, 'default') == (
Translation._cache_key(1, 'default'))

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

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

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

@ -360,7 +360,7 @@ class UserProfile(OnChangeMixin, ModelBase, AbstractBaseUser):
self.addonuser_set.filter(
role__in=[amo.AUTHOR_ROLE_OWNER, amo.AUTHOR_ROLE_DEV],
listed=True,
addon__status=amo.STATUS_PUBLIC).no_cache().exists())
addon__status=amo.STATUS_PUBLIC).exists())
if is_public != pre:
log.info('Updating %s.is_public from %s to %s' % (
self.pk, pre, is_public))

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

@ -10,7 +10,6 @@ from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import ugettext
import caching.base
import jinja2
from django_extensions.db.fields.json import JSONField
@ -358,7 +357,7 @@ class Version(OnChangeMixin, ModelBase):
def all_activity(self):
from olympia.activity.models import VersionLog # yucky
al = (VersionLog.objects.filter(version=self.id).order_by('created')
.select_related('activity_log', 'version').no_cache())
.select_related('activity_log', 'version'))
return al
@property
@ -549,11 +548,9 @@ class Version(OnChangeMixin, ModelBase):
return
ids = set(v.id for v in versions)
# FIXME: find out why we have no_cache() here and try to remove it.
avs = (ApplicationsVersions.objects.filter(version__in=ids)
.select_related('min', 'max')
.no_cache())
files = File.objects.filter(version__in=ids).no_cache()
.select_related('min', 'max'))
files = File.objects.filter(version__in=ids)
def rollup(xs):
groups = sorted_groupby(xs, 'version_id')
@ -578,7 +575,7 @@ class Version(OnChangeMixin, ModelBase):
return
al = (VersionLog.objects.filter(version__in=ids).order_by('created')
.select_related('activity_log', 'version').no_cache())
.select_related('activity_log', 'version'))
def rollup(xs):
groups = sorted_groupby(xs, 'version_id')
@ -612,8 +609,6 @@ class Version(OnChangeMixin, ModelBase):
nomination = nomination or datetime.datetime.now()
# We need signal=False not to call update_status (which calls us).
self.update(nomination=nomination, _signal=False)
# But we need the cache to be flushed.
Version.objects.invalidate(self)
def inherit_nomination(self, from_statuses=None):
last_ver = (Version.objects.filter(addon=self.addon,
@ -801,7 +796,6 @@ models.signals.post_delete.connect(
class LicenseManager(ManagerBase):
def builtins(self, cc=False):
return self.filter(
builtin__gt=0, creative_commons=cc).order_by('builtin')
@ -842,7 +836,7 @@ models.signals.pre_save.connect(
save_signal, sender=License, dispatch_uid='license_translations')
class ApplicationsVersions(caching.base.CachingMixin, models.Model):
class ApplicationsVersions(models.Model):
application = models.PositiveIntegerField(choices=amo.APPS_CHOICES,
db_column='application_id')
@ -853,8 +847,6 @@ class ApplicationsVersions(caching.base.CachingMixin, models.Model):
max = models.ForeignKey(AppVersion, db_column='max',
related_name='max_set')
objects = caching.base.CachingManager()
class Meta:
db_table = u'applications_versions'
unique_together = (("application", "version"),)

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

@ -149,7 +149,7 @@ class TestVersion(TestCase):
assert version.files.count() == 1
version.delete()
addon = Addon.objects.no_cache().get(pk=3615)
addon = Addon.objects.get(pk=3615)
assert not Version.objects.filter(addon=addon).exists()
assert Version.unfiltered.filter(addon=addon).exists()
assert version.files.count() == 1
@ -162,7 +162,7 @@ class TestVersion(TestCase):
assert version.files.count() == 1
version.delete(hard=True)
addon = Addon.objects.no_cache().get(pk=3615)
addon = Addon.objects.get(pk=3615)
assert not Version.objects.filter(addon=addon).exists()
assert not Version.unfiltered.filter(addon=addon).exists()
assert version.files.count() == 0
@ -226,7 +226,7 @@ class TestVersion(TestCase):
file = File(platform=amo.PLATFORM_MAC.id, version=version)
file.save()
# The transform don't know bout my new files.
version = Version.objects.no_cache().get(pk=81551)
version = Version.objects.get(pk=81551)
assert not version.is_allowed_upload()
def test_version_is_not_allowed_upload_full(self):

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

@ -37,10 +37,6 @@ def do_removeuser(user, group):
# Doesn't actually check if the user was in the group or not.
GroupUser.objects.filter(user=user, group=group).delete()
# Help django-cache-machine invalidate its cache (it has issues with
# M2Ms).
Group.objects.invalidate(*user.groups.all())
except UserProfile.DoesNotExist:
raise CommandError('User ({user}) does not exist.'.format(user=user))
except Group.DoesNotExist:

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

@ -5,21 +5,17 @@ from decimal import Decimal
from django.conf import settings
from django.db import models
import caching
from olympia import amo
from olympia.amo.models import ModelBase
from olympia.applications.models import AppVersion
from olympia.files.models import File
class Config(caching.base.CachingMixin, models.Model):
class Config(models.Model):
"""Sitewide settings."""
key = models.CharField(max_length=255, primary_key=True)
value = models.TextField()
objects = caching.base.CachingManager()
class Meta:
db_table = u'config'
@ -38,7 +34,7 @@ def get_config(conf):
try:
return Config.objects.get(key=conf).value
except Config.DoesNotExist:
return
return None
def set_config(conf, value):