Рекурсивная недействительность пути из кеша Django

Я удаляю один путь из кеша Django следующим образом:

from models                   import Graph
from django.http              import HttpRequest
from django.utils.cache       import get_cache_key
from django.db.models.signals import post_save
from django.core.cache        import cache

def expire_page(path):
    request      = HttpRequest()
    request.path = path
    key          = get_cache_key(request)
    if cache.has_key(key):   
        cache.delete(key)

def invalidate_cache(sender, instance, **kwargs):
    expire_page(instance.get_absolute_url())

post_save.connect(invalidate_cache, sender = Graph)

Это работает, но есть ли способ рекурсивного удаления? Мои пути выглядят так:

/graph/123
/graph/123/2009-08-01/2009-10-21

Всякий раз, когда граф с идентификатором «123» сохраняется, кеш для обоих путей должен быть аннулирован. Можно ли это сделать?


person knipknap    schedule 03.01.2010    source источник
comment
Я не уверен, правильно ли я понял ваш вопрос, вы имеете в виду, что хотите очистить весь кеш, кроме одного с идентификатором 123?   -  person piyer    schedule 03.01.2010
comment
Я хочу очистить кеш для любого пути, начинающегося с «/graph/123/».   -  person knipknap    schedule 03.01.2010
comment
Я не понимаю, почему вы беспокоитесь о пути?   -  person piyer    schedule 03.01.2010
comment
Потому что Django использует путь запроса для создания ключа для кеша. Как вы думаете, что непонятно?   -  person knipknap    schedule 03.01.2010


Ответы (3)


Возможно, вы захотите рассмотреть возможность использования стратегии кэширования поколений, похоже, она подойдет для того, чего вы пытаетесь достичь. В предоставленном вами коде вы должны сохранить номер «поколения» для каждого абсолютного URL-адреса. Так, например, вы должны инициализировать "/graph/123", чтобы иметь поколение один, тогда его ключ кэша станет чем-то вроде "/GENERATION/1/graph/123". Когда вы хотите истечь срок действия кеша для этого абсолютного URL-адреса, вы увеличиваете значение его генерации (в данном случае до двух). Таким образом, в следующий раз, когда кто-то будет искать «/graph/123», ключ кэша станет «/GENERATION/2/graph/123». Это также решает проблему истечения срока действия всех подстраниц, поскольку они должны ссылаться на тот же ключ кэша, что и «/graph/123».

Сначала это немного сложно понять, но это действительно элегантная стратегия кэширования, которая, если все сделано правильно, означает, что вам никогда не придется ничего удалять из кеша. Дополнительную информацию можно найти здесь: презентация по кэшированию поколений, для Rails, но концепция одна и та же, независимо от языка.

person jkupferman    schedule 28.01.2010
comment
Таким образом, вы говорите, что ключ кеша /graph/123 всегда будет существовать и содержать содержимое /gen/1/graph/123 или /gen/2/graph/123 и т. д., поэтому приложение сначала запрашивает /graph/123, чтобы получить ключ самого последнего ключа генерации, а затем снова запрашивает кеш с помощью /gen/x/graph/123 чтобы получить самый последний кэшированный контент? Это сложная часть, потому что, если это не так, как приложение запрашивает кеш, если ключ кеша всегда меняется с новым поколением. - person raffian; 04.09.2012

Другой вариант — использовать кеш, который поддерживает тегирование ключей и удаление ключей по тегу. Встроенный API кеша Django не поддерживает этот подход. Но по крайней мере один кеш-бэкенд (не являющийся частью собственно Django) имеет поддержку.

DiskCache* — это лицензированный Apache2 дисковый и файловый кэш. библиотека, написанная на чистом Python и совместимая с Django. Чтобы использовать DiskCache в своем проекте, просто установите его и настройте параметр CACHES.

Установка проста с pip:

$ pip install diskcache

Затем настройте параметр CACHES:

CACHES = {
    'default': {
        'BACKEND': 'diskcache.DjangoCache',
        'LOCATION': '/tmp/path/to/directory/',
    }
}

Метод cache set расширяется необязательным аргументом ключевого слова tag следующим образом:

from django.core.cache import cache

cache.set('/graph/123', value, tag='/graph/123')
cache.set('/graph/123/2009-08-01/2009-10-21', other_value, tag='/graph/123')

diskcache.DjangoCache использует diskcache.FanoutCache внутри. Соответствующий FanoutCache доступен через атрибут _cache и предоставляет метод evict. Чтобы удалить все ключи с тегом /graph/123, просто:

cache._cache.evict('/graph/123')

Хотя доступ к атрибуту с префиксом подчеркивания может показаться неудобным, проект DiskCache стабилен и вряд ли внесет существенные изменения в реализацию DjangoCache.

На странице тестов кэша Django есть обсуждение альтернативных бэкендов кэша.

  • Отказ от ответственности: я являюсь первоначальным автором проекта DiskCache.
person GrantJ    schedule 21.03.2016

Оформить заказ shutils.rmtree() или os.removedirs(). Я думаю, что первое, вероятно, то, что вы хотите.

Обновление на основе нескольких комментариев: на самом деле механизм кэширования Django является более общим и точным, чем просто использование path для ключа (хотя вы можете использовать его на этом уровне). У нас есть некоторые страницы с 7 или 8 отдельно кэшированными подкомпонентами, срок действия которых истекает в зависимости от ряда критериев. Имена кэшей наших компонентов отражают ключевые объекты (или классы объектов) и используются для определения того, что необходимо аннулировать при определенных обновлениях.

Все наши страницы имеют общий кеш-ключ, основанный на статусе члена/не члена, но это только около 95% страницы. Остальные 5% могут изменяться для каждого члена, поэтому они вообще не кэшируются.

То, как вы просматриваете свой кеш, чтобы найти недопустимые элементы, зависит от того, как он на самом деле хранится. Если это файлы, вы можете использовать просто globs и/или рекурсивное удаление каталогов, если это какой-то другой механизм, вам придется использовать что-то еще.

Мой ответ и некоторые комментарии других пытаются сказать, что то, как вы выполняете инвалидацию кеша, тесно связано с тем, как вы используете/храните кеш.

Второе обновление: @andybak: Значит, ваш комментарий означает, что все мои коммерческие сайты Django взорвутся в огне? Спасибо за внимание к этому. Я заметил, что вы не пытались ответить на проблему.

Проблема Knipknap заключается в том, что у него есть группа элементов кеша, которые кажутся связанными и находятся в иерархии из-за их имен, но логика генерации ключей механизма кеша стирает это имя, создавая хэш MD5. пути + Vari_on. Поскольку нет следов исходного пути/параметров, вам придется полностью угадать все возможные комбинации пути/параметров, надеясь, что вы сможете найти правильную группу. У меня есть другие увлечения, более интересные.

Если вы хотите иметь возможность находить группы кэшированных элементов на основе некоторой комбинации значений пути и/или параметра, вы должны либо использовать ключи кэша, которые могут быть сопоставлены с образцом напрямую, или некоторая система, которая сохраняет эту информацию для использования во время поиска.

Поскольку у нас были потребности, не связанные с проблемой OP, мы взяли под свой контроль кэширование фрагментов шаблона — и, в частности, генерацию ключей — более 2 лет назад. Это позволяет нам использовать регулярные выражения несколькими способами, чтобы эффективно аннулировать группы связанных кэшированных элементов. Мы также добавили тайм-аут по умолчанию и имена переменных Vari_on (разрешенные во время выполнения), настраиваемые в settings.py, изменили порядок имени и тайм-аута, потому что не было смысла всегда переопределять тайм-аут по умолчанию, чтобы назвать фрагмент, сделали имя_фрагмента resolvable (т. е. это может быть переменная), чтобы лучше работать с многоуровневой схемой наследования шаблонов, и несколько других вещей.

Единственная причина моего первоначального ответа, который действительно был неверным для текущего Django, заключалась в том, что я так долго использовал более разумные ключи кеша, что буквально забыл простой механизм, от которого мы отказались.

person Peter Rowell    schedule 03.01.2010
comment
Не совсем то, что я имел в виду. Спасибо хоть. - person knipknap; 03.01.2010
comment
Ну, тогда вам нужно быть более конкретным. Где твой кеш? Файлы? кэширование памяти? База данных? Если они в файлах, то мой ответ в правильном направлении. Если они находятся где-то еще, вам нужен какой-то механизм для сопоставления всех кэшированных элементов, содержащих ваш измененный объект. Мы решили эту проблему (и несколько других проблем с аннулированием кеша), реализовав собственный механизм кэширования. Это не так сложно сделать. - person Peter Rowell; 03.01.2010
comment
Извините, что понизил голос, но исходный вопрос был совершенно ясен всем, кто использует Django! - person Andy Baker; 07.01.2010