Rails не может кэшировать предварительно вычисленные значения по запросам браузера? (например, запоминание n факторных результатов)

#ruby-on-rails #ruby #ruby-on-rails-3 #memoization

#ruby-on-rails #ruby #ruby-on-rails-3 #запоминание

Вопрос:

Например, следующий код:

 class FoosController < ApplicationController
  def index
    if !@foo.nil?
      render :locals => {:bar => @foo}
      return
    else 
      @foo = rand 10
      render :locals => {:bar => @foo}
    end
  end
end
  

если я загружу localhost:3000/foos несколько раз, он будет показывать разные значения, и неудивительно, что это режим разработки, потому что Rails перезагружает контроллер (а также модель и представление) каждый раз, когда поступает запрос браузера.

Но даже в рабочем режиме, когда все загружено и остается там, @foo значение s не будет сохраняться в запросах браузера? Каждый раз при перезагрузке страницы в веб-браузере отображается другое число. Значит, Rails будет каждый раз очищать все значения? Есть ли способ кэшировать или «запоминать» результаты по запросам, если мы не используем СУБД?


Удивительно, но я только что попробовал использовать переменную класса, и в режиме разработки она каждый раз выдает другое число. В рабочем режиме число остается неизменным в Firefox, и тогда Chrome также будет показывать это число все время, пока сервер не будет перезапущен:

 class FoosController < ApplicationController
  @@foo = nil

  def index
    if !@@foo.nil?
      render :locals => {:bar => @@foo}
      return
    else 
      @@foo = rand 10
      render :locals => {:bar => @@foo}
    end
  end
end
  

Почему переменная класса может запоминаться, а переменная экземпляра — нет? Является ли использование переменной класса надежным способом надежно «запоминать вещи» по запросу в Rails 2.x, 3.x и Ruby 1.8.7 и 1.9.2?

Ответ №1:

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

Возможно, вы захотите использовать session или flash хэши для хранения @foo между запросами.

 class FoosController < ApplicationController
  def index
    @foo = session[:foo] ||= rand(10)
    render :locals => {:bar => @foo}
  end
end
  

session[:foo] будет сохраняться на протяжении всего пути, но вы можете использовать flash[:foo] , если хотите сохранить его только в одном запросе.

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

1. Обратите внимание, что сеанс по умолчанию сохраняется в файлах cookie, которые хранятся на компьютере пользователя (не помещайте туда конфиденциальные данные), и имеют ограниченный размер файла (что-то вроде 2 или 4 Кб). Кроме того, у каждого пользователя есть уникальный сеанс, поэтому кэширование будет осуществляться для данного пользователя, но не для всех пользователей.

Ответ №2:

@@foo обозначает переменную класса, @foo переменную экземпляра. При каждом запросе создается новый экземпляр контроллера. А для кэширования я действительно рекомендую memcache или что-то подобное (из-за разветвленной природы производственных серверов rails).

Ответ №3:

Даже если вы изменяете настройки так, как предлагает edgerunner, в рабочей среде у вас может быть запущено несколько экземпляров mongrel или thin, и каждый из этих контроллеров будет иметь разные переменные flash, и текущий подход не будет работать. Продолжайте с тем, что говорит Tass, что-то вроде memcache или redis для сохранения таких данных в памяти.

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

1. Kunday по умолчанию session и flash сохраняются в файлах cookie, поэтому несколько экземпляров не будут проблемой. Другими вариантами хранения сеанса являются Memcached и database, которые также позволяют избежать проблемы с несколькими экземплярами. Фактически, единственный упомянутый здесь подход, который потерпит неудачу в нескольких экземплярах, — это подход с переменной класса в исходном вопросе.

2. Спасибо, что исправил меня, Edgerunner. Но сохранение в сеансе [:foo] ограничено одним браузером, верно? Я предполагаю, что хранение в Memcache или базе данных будет осуществляться в нескольких браузерах, и если алгоритм сложный (вычисление для каждого уникального сеанса браузера по-прежнему дорого, верно?), я думаю, нам все равно следует использовать какой-нибудь memcached или database, верно? Поправьте меня, если я ошибаюсь.

3. Я бы предположил, что если вы хотите сохранить что-то в запросах, в большинстве случаев это последовательные запросы одного пользователя. Хранилище файлов cookie, подходящее для такого сценария. Однако, если вы хотите что-то накопительное, вам, вероятно, следует сохранить в базе данных. Memcached быстр, постоянен и хорош, но даже он не является действительно постоянным. А memcached обходится дорого, если вы хотите сохранить много данных. В любом случае фактический выбор имеет несколько параметров, подлежащих анализу. На это трудно дать окончательный ответ.

4. Согласовано. Я никогда не сталкивался со сценарием, в котором был бы эквивалентный тестовый пример. Раньше у меня была некоторая работа, которая требовала таких вещей. Надеюсь, что работа над этим даст некоторое представление. Между memcached прав ли локальный сервер, сообщая о его дороговизне, вы упоминаете стоимость memcached heroku?

5. Memcached — это непостоянное хранилище в памяти, и каждый байт памяти всегда дороже, чем каждый байт диска. Memcached не гарантирует надежного сохранения каких-либо данных. При перезапуске сервера произойдет сбой. Как следует из названия, это хранилище кэша, которое помогает ускорить процесс. Это не постоянное хранилище.