Rails Conditional Получить не правильно себя ведет на Heroku

У меня есть приложение Rails 3.2, где клиентская сторона запрашивает обновления на сервере. Этот ресурс не актив, а динамический контент.

Стратегия реализации, которую я выбрал, - это условное получение с помощью директивы fresh_when:

fresh_when(:etag => @etag, :public => false ) #it's a private resource, requires auth etc

Таким образом, когда ресурс еще свежий (заголовок запроса If-Not-Modified равен текущему ETag ресурса), сервер возвращает только заголовок 304.

Started GET "/news/4fe13e74aa5e7d3d70000001"
Processing by NewsController#show as JSON
Parameters: {"id"=>"4fe13e74aa5e7d3d70000001"}
Completed 304 Not Modified

Когда ресурс больше не свежий, у нас есть статус 200 и самая последняя версия ресурса в теле ответа:

Started GET "/news/4fe13e74aa5e7d3d70000001"
Processing by NewsController#show as JSON
Parameters: {"id"=>"4fe13e74aa5e7d3d70000001"}
Rendered news/show.json.rabl (8.1ms)
Completed 200 OK in 14ms

В среде разработки это работает отлично. Проблема заключается в производственной среде (Heroku Cedar Stack). В этом сценарии ответы всегда 200 с полным представлением объекта в теле:

2012-08-03T21:44:33+00:00 heroku[router]: GET blah.com/news/4fa43b428b91cd0001000002 dyno=web.1 queue=0 wait=0ms service=22ms status=200 bytes=2105

2012-08-03T21:44:33+00:00 app[web.1]: Started GET "/news/4fa43b428b91cd0001000002"
2012-08-03T21:44:33+00:00 app[web.1]: cache: [GET /news/4fa43b428b91cd0001000002] miss

2012-08-03T21:44:33+00:00 heroku[nginx]: 187.38.19.138 - - [03/Aug/2012:21:44:33 +0000] "GET /news/4fa43b428b91cd0001000002 HTTP/1.1" 200 665 "http://www.bla.com/news/4fa43b428b91cd0001000002" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.57 Safari/537.1" 

Судя по всему, этот запрос не доходит до Rails Controller (где на самом деле оценивается свежесть), а проходит через уровни роутера и кэширования heroku.

Что я пробовал до сих пор:

  • Версии Rails (3.2.1) и Thin (1.3.1) дважды проверены в обеих средах.
  • Установка 'config.action_controller.perform_caching = false'
  • Удаление промежуточного программного обеспечения Rack::Cache (config.middleware.delete Rack::Cache)

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

Спасибо

1 ответ

Решение

Корень проблемы лежит в неправильном использовании API. Эта строка:

fresh_when(etag: @etag, public: false)

Ведёт себя как положено в разработке, но не в производстве. Я подумал, что обмена заголовками Etag/If-Not-Modified было достаточно для такого рода процедуры условного получения, поэтому мне было все равно, добавить ли Last-Modified.

После прочтения RFC HTTP 1.1, в котором говорится: "Серверы HTTP/1.1 ДОЛЖНЫ отправлять Last-Modified, когда это возможно". я решил попробовать

fresh_when(etag: @etag, public: false, last_modified: @news.updated_at)

И это сработало в Heroku! Я на 99% уверен, что отсутствие этого заголовка каким-то образом испортило путь запроса / ответа в их стеке (учитывая, что в передней части вашего dyno есть другие серверы, такие как Varnish и Nginx)

Другие вопросы по тегам