Suor/django-cacheops — pull request #27 by tumb1er (2013-01-24 11:43:21+00:00)
Title: Watch-free invalidation

Привет! Мы тут использовали cacheops 0.9.3, и при высокой конкурентности получили бесконечные перезапуски инвалидации из-за того, что вся транзакция обламывалась с WatchError. В результате Redis-server занимал 100% CPU одного ядра, при этом сильно возрастал (в разы!) трафик между всеми приложениями и сервером редиса. В логе сервера (monitor) при этом видится нормальная работа по добавлению новых ключей в конъюнкции, и лишь мельком проглядывают команды инвалидации. С 0.9.1 такого не было, т.к. первые три обращения к редису там выполнялись батчем, что сильно снижало вероятность того, что кто-то другой вклинится в процесс инвалидации текущего объекта. В 0.9.3 метод invalidate_by_dict уже делает 4 обращения к редису, поэтому все очень плохо.
Подумав немножко (2 дня)), мы решили убрать WATCH вообще из инвалидации, сделав получение версии схемы, получение списка инвалидируемых ключей (q:...) и удаление самих конъюнкций атомарной операцией. При этом исходили из предположения, что если в инвалидируемые конъюнкции кто-то другой успеет добавить ключи, из-за MULTI-EXEC результат будет аналогичный тому, что эти ключи будут добавлены уже после завершения всей инвалидации.
Сами ключи "q" удаляются уже после завершения транзакции, если при этом упадет процесс питона, ключи повиснут на Expire Time без своих инвалидаторов.
После завершения транзакции пост-фактум проверяется версия схемы, мы исходили из предположения, что версия схемы меняется очень редко. У нас максимум - 13.
https://github.com/tumb1er/django-cacheops/commit/18f9290d1f333d2205f175063b0d93c239e0b4f7 вот в этом коммите добавлен тест, демонстрирующий race-condition в момент инвалидации, что собственно и происходило у нас на продакшне. На новой версии тест проходит успешно ).
Очень хотим увидеть версию 0.9.4 ;-)