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)