Rails 3.0.3 Rails.cache.read не может выполнить запись в БД

#ruby-on-rails #activerecord

#ruby-on-rails #activerecord

Вопрос:

Я играю с функцией Rails.cache в Rails 3, и когда я записываю запись ActiveRecord в cache, я не могу прочитать ее обратно, изменить атрибуты, а затем записать в базу данных. Я получаю сообщение «Ошибка типа: не удается изменить замороженный хэш». Раньше я использовал другой плагин memcache, но я пытаюсь переключиться на Heroku, и меня невероятно раздражает, что я не могу сохранить записи ActiveRecord, которые я добавляю в Memcache. Это приведет к большому количеству ненужных операций чтения из БД для изменения небольших фрагментов информации.

Например, я мог бы сделать это в базе данных. Предполагая, что пользовательская модель является:

Пользователь -> логин: строка и ввод следующего в rails c

 user = User.new
user.login = 'test'
user.save
Rails.cache.write('user:login:test', user)
user2 = Rails.cache.read('user:login:test')
user2.login = 'test2'
TypeError: can't modify frozen hash
/app/.bundle/gems/ruby/1.8/gems/activerecord-3.0.3/lib/active_record/attribute_methods/write.rb:26:in `[]='
    /app/.bundle/gems/ruby/1.8/gems/activerecord-3.0.3/lib/active_record/attribute_methods/write.rb:26:in `write_attribute'
    /app/.bundle/gems/ruby/1.8/gems/activerecord-3.0.3/lib/active_record/attribute_methods/dirty.rb:61:in `write_attribute'
    /app/.bundle/gems/ruby/1.8/gems/activerecord-3.0.3/lib/active_record/attribute_methods/write.rb:13:in `login='
  

Кто-нибудь знает, как решить эту проблему?

Комментарии:

1. Вы пробовали 3.0.6, чтобы узнать, по-прежнему ли у вас возникает эта проблема?

Ответ №1:

После получения значения объекта user2 из этой строки:

 user2 = Rails.cache.read('user:login:test')
  

выполните следующее:

 dup_user = user2.dup # this will make a clone of the user2 object
dup_user.login = test_2
  

Тогда исключение «замороженный хэш» больше не должно появляться.

Ответ №2:

Когда вы сохраняете объект в кэше, объект замораживается. Это одна из причин, по которой вы никогда не должны хранить сложные объекты, такие как модели ActiveRecord.

При чтении из кэша объект загружается, но он все еще заморожен. ActiveRecord пытается восстановить состояние объекта, но для этого необходимо обновить некоторые внутренние атрибуты. Обновление завершится ошибкой, поскольку объект заморожен.

Вместо того чтобы сохранять весь объект целиком, просто сохраните его идентификатор и извлеките пользователя «на лету».

 user = User.new
user.login = 'test'
user.save
Rails.cache.write('user:login:test', user.id)
user2 = User.find(Rails.cache.read('user:login:test'))
user2.login = 'test2'
  

Это немного менее эффективно с точки зрения кэширования, но это правильный путь. На самом деле, вы не учитываете, что за это время могли быть изменены другие атрибуты пользователя, и ваш объект больше не представляет текущее состояние.

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

Конечно, вы можете использовать #dup обходной путь, но это всего лишь уловка. Это не решение. У вас все равно будут проблемы с целостностью кэша, как я объяснил выше.

 user = User.new
user.login = 'test'
user.save
Rails.cache.write('user:login:test', user)
user2 = Rails.cache.read('user:login:test').dup
user2.login = 'test2'