Caching and Performance
Dragonfly serves content on-the-fly, that is, when a request is made to a url given by
url = Dragonfly.app.fetch('my/stored/image').thumb('300x200').url
then Dragonfly will do exactly what the job says, i.e.
- fetch the content stored with uid “my/stored/image”
- process it to make a thumbnail with dimensions 300x200
Doing this on every request is expensive, so it’s wise to use a caching proxy, so that after the first request the server simply returns the cached response.
Dragonfly sends Cache-Control
and ETag
headers in its responses to let us do this (for a well-written discussion of this, see this blog post).
Simply put a proxy like Rack::Cache, Varnish or Squid in front of the app and subsequent requests will be served super-quickly straight out of the cache.
For simple use of Rack::Cache with rails see Rails.
To customize Dragonfly’s response headers, inside a configure
block, you can use something like
response_header "Cache-Control", "public, max-age=3600"
or
response_header "Cache-Control" do |job, request, headers|
if job.fetch_step && job.fetch_step.uid =~ /2012/
"public, max-age=100000"
else
"private"
end
end
Setting the header to nil
removes the header from the response.
Using a CDN
Some CDNs are configured to make a request to your domain on the first request and return the cached version thereafter, using the same url path.
You can use url_host
to point urls to the CDN instead of your local server.
For example, a url
Dragonfly.app.fetch('some/uid').thumb('300x200').url
normally returns something like
"/W1siZiIsInNvbWUvdWlkIl0sWyJwIiwidGh1bWIiLCIzMDB4MjAwIl1d"
Adding in a configure
block
url_host 'http://some.cdn'
makes the url
"http://some.cdn/W1siZiIsInNvbWUvdWlkIl0sWyJwIiwidGh1bWIiLCIzMDB4MjAwIl1d"
NOTE: some CDNs may not forward GET parameters, in which case you may need to make sure the sha
parameter is part of the path, i.e. in a configure
block
url_format "/media/:job/:sha/:name"
Serving remotely (directly from the datastore)
See URLs
Performing processing on upload
Processing on-the-fly and serving remotely
Serving processed versions of content such as thumbnails remotely is a bit more tricky as we need to upload the thumbnail to the datastore in the on-the-fly manner.
Dragonfly provides a way of doing this using define_url
and before_serve
methods.
The details of keeping track of/expiring these thumbnails is up to you.
We need to keep track of which thumbnails have been already created, by storing a uid for each one.
Below is an example using an ActiveRecord Thumb
table to keep track of already created thumbnail uids.
It has two string columns; signature
and uid
.
Dragonfly.app.configure do
# Override the .url method...
define_url do |app, job, opts|
thumb = Thumb.find_by_signature(job.signature)
# If (fetch 'some_uid' then resize to '40x40') has been stored already, give the datastore's remote url ...
if thumb
app.datastore.url_for(thumb.uid)
# ...otherwise give the local Dragonfly server url
else
app.server.url_for(job)
end
end
# Before serving from the local Dragonfly server...
before_serve do |job, env|
# ...store the thumbnail in the datastore...
uid = job.store
# ...keep track of its uid so next time we can serve directly from the datastore
Thumb.create!(uid: uid, signature: job.signature)
end
end
This would give
app.fetch('some_uid').thumb('40x40').url # normal Dragonfly url e.g. /media/WhbBls...
then from the second time onwards
app.fetch('some_uid').thumb('40x40').url # http://my-bucket.s3.amazonaws.com/2011...
The above is just an example - there are a number of things you could do with before_serve
and define_url
-
you could use e.g. Redis or some key-value store to keep track of thumbnails.
You’d also probably want a way of expiring the thumbnails or destroying them when the original is destroyed, but this is left up to you as it’s outside of the scope of Dragonfly.
Derived from theme by orderedlist