Это может быть немного многословно, но мне пришлось потратить некоторое время с исходным кодом Rails, чтобы узнать, как работает внутреннее кэширование. Записывание вещей помогает моему пониманию, и я полагаю, что обмен некоторыми заметками о том, как все работает, не может повредить. Если вы спешите, переходите до конца.
Почему это происходит
Это неправильный метод в ActiveSupport:
def should_compress?(value, options)
if options[:compress] && value
unless value.is_a?(Numeric)
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
serialized_value = value.is_a?(String) ? value : Marshal.dump(value)
return true if serialized_value.size >= compress_threshold
end
end
false
end
Обратите внимание на присвоение serialized_value
. Если вы покопаетесь внутри cache.rb
, вы увидите, что он использует Marshal для сериализации объектов в байтовые строки перед тем, как они попадут в кэш, а затем снова маршал для десериализации объектов. Проблема сжатия здесь не важна, важно использовать маршала.
Проблема в том, что :
Некоторые объекты не могут быть выгружены: если объекты, которые должны быть выгружены, включают в себя привязки, объекты процедур или методов, экземпляры класса IO или одноэлементные объекты, будет вызвано TypeError.
Некоторые вещи имеют состояние (например, дескрипторы файлов ОС или блоки), которые не могут быть сериализованы маршалом. Вы видите ошибку:
не может сбросить хэш с процедурой по умолчанию
Итак, у кого-то в вашей модели есть переменная экземпляра, которая является Hash, и этот Hash использует блок для предоставления значений по умолчанию. Метод column_methods_hash
использует такой хэш и даже кэширует хеш внутри @dynamic_methods_hash
; column_methods_hash
будет вызываться (косвенно) открытыми методами, такими как respond_to?
и method_missing
.
Один из respond_to?
или method_missing
, вероятно, рано или поздно будет вызван на каждом экземпляре модели AR, и вызов любого из этих методов сделает ваш объект не сериализуемым. Таким образом, экземпляры AR-моделей в Rails 3 практически не могут быть проанализированы.
Интересно, что реализации respond_to?
и method_missing
в 2.3.8 также поддерживаются хэшем, который использует блок для значений по умолчанию. Кеш 2.3.8 «[...] предназначен для кэширования строк.» , так что вам повезло с бэкэндом, который может обрабатывать целые объекты, или он использовал Marshal до того, как ваши объекты имели хеш-с- процы в них; или, возможно, вы использовали MemoryStore
бэкэнд кеша, и это немного больше, чем большой хэш.
Использование нескольких scope-with-lambdas может привести к хранению Procs в ваших объектах AR; Я ожидал, что лямбды будут храниться с классом (или синглтон-классом), а не с объектами, но я не стал заниматься анализом, поскольку проблема с respond_to?
и method_missing
делает проблему scope
неактуальной.
Что вы можете с этим сделать
Я думаю, что вы хранили неправильные вещи в своем кеше и вам повезло. Вы можете либо начать использовать кеш Rails должным образом (т.е. хранить простые сгенерированные данные, а не целые модели), либо можете реализовать методы marshal_dump
/ marshal_load
или _dump
/ _load
, как описано в Marshal . Кроме того, вы можете использовать один из бэкэндов MemoryStore и ограничить себя одним отдельным кешем для каждого процесса сервера.
Резюме
Вы не можете зависеть от хранения объектов модели ActiveRecord в кэше Rails, если вы не готовы самостоятельно выполнять маршалинг или не хотите ограничивать себя бэкэндами кэша MemoryStore.