I'm using Dragonfly to serve processed images for my Rails app. Dragonfly relies on Rack::Cache for future visits to those processed images, so that Dragonfly won't have to process those images again and again, thus wasting CPU time.
My problem starts here: if I'm right that sending a file via Rack::Cache still busies a Rails process, then viewing a page of 30 images, even if these images have a small file size, will tie up the Rails processes pretty quickly. If a couple more visitors come to see that page, then they will experience very slow response times. How do I get these files served via X-Sendfile?
I've set the following in production.rb
, but I know these are for the assets from Rails, not the Dragonfly files:
config.serve_static_assets = false
config.action_dispatch.x_sendfile_header = "X-Sendfile"
I know that Rack::Cache somehow supports X-Sendfile (probably through Rack::Sendfile) because it produces a body that responds to #to_path
. However, I don't know how to enable this. When I check files that come from Rack::Cache, I don't see any X-Sendfile information:
Date: Wed, 02 Nov 2011 11:38:28 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.9
Content-Disposition: filename="2.JPG"
Cache-Control: public, max-age=31536000
Etag: "3174d486e4df2e78a5ff9174cacbede5787d4660"
X-Content-Digest: c174408eda6e689998a40db0aef4cdd2aedb3b6c
Age: 28315
X-Rack-Cache: fresh
Content-Length: 22377
Status: 200
Content-Type: image/jpeg
I know, based on posts around the net, that I'm supposed to see something like:
X-Sendfile: /path/to/file
In the end I don't know if its Dragonfly or Rack::Cache (or both) that I have to configure. How do I get either Dragonfly and/or Rack::Cache to serve files via X-Sendfile?
Info about my setup:
- Rails 3.1.1
- Passenger 3.0.9
- CentOS
- Sendfile module is installed, as far as I know. I have
XSendFile On
andXSendFilePath /path/to/app
specified in my virtualhost configuration, and Apache doesn't complain about the directiveXSendFile
not existing.
Thanks!
UPDATE Nov 6, 2011
Based on this old update, as long as Rack::Sendfile
is placed in front of Rack::Cache
, then X-Sendfile will be used. I did that, and this is how my middleware looks like. The files, however, still don't have the X-Sendfile tag. Again, I don't know if that is a sure-fire way of determining if X-Sendfile is enabled, so I checked the Passenger queue. It seems that the queue is greatly encumbered when I visit a page.
UPDATE Nov 7, 2011
It seems this is purely a Rack::Cache and Rails 3.1 issue. While Rack::Cache supports the use of X-Sendfile through Rack::Sendfile (like I mentioned above, Rack::Cache, when using the Disk EntityStore since that responds_to to_path
since the body it returns is a subclass of File), Rails 3.1 uses its own storage solution. Rails 3.1 uses ActiveSupport::Cache::FileStore, which is set by default, if you don't specify anything in your production.rb
file.
The problem with FileStore is that the body it returns, to be part of the response to be sent upstream, because that body doesn't respond to to_path
. The body is an instance of ActiveSupport::Cache::Entry. You can see here that when the FileStore is asked to read a cached file, it reads it via File.open('/path/to/file') {|f| Marshal.load(f) }
which returns an instance of Entry. The value that ultimately gets passed upstream and back to the client, is Entry#value.
My questions
To help me decide whether I should patch this, or to get Rails to use Rack::Cache's own Disk store instead, I have some questions:
- What's the reason Rack::Cache's own storage solutions weren't used for Rails 3.1? Why does Rails have its own?
- Is there a reason Marshal is used? Is there a reason that a bytestream of data should be sent back instead?
I got in deeper than I usually go, and will be surprised if I understood things correctly. I hope to find an answer!
As an alternative to Varnish, you can use Apache's mod_disk_cache. It would be less work to set up as you are already running Apache.