This project is archived and is in readonly mode.

#3927 open
tadman

Rails 3.0.0-pre - stylesheet_link_tag, javascript_include_tag cache fails when using File.atomic_write: Invalid cross-device link

Reported by tadman | February 11th, 2010 @ 05:37 AM | in 3.0.6

In Rails 3.0.0-pre, when caching JavaScript and CSS assets, a mysterious error is generated on systems with a separate partition for temporary files.

ActionView::Helpers.write_asset_file_contents makes use of File.atomic_write with no tmp_dir parameter specified. This defaults to Dir.tmpdir accordingly, however, it appears that if this directory is on a different volume than the Rails application then an error will result when apparently trying to create a hard-link to move the cached asset file:

ActionView::Template::Error (Invalid cross-device link - /tmp//_main.css20100210-24481-ilznwv-0 or public/stylesheets/_main.css)

This was generated from a simple asset-caching request:

stylesheet_link_tag('reset', 'master', 'forms', :cache => '_main')

This would only seem to be an issue on systems where Dir.tmpname returns a path on a different volume than where Rails.root is situated. On some Linux installations /tmp is given its own partition.

A simple patch is to use the default Rails tmp/ directory by modifying the call in write_asset_file_contents to be:

module ActionView
  module Helpers
    def write_asset_file_contents(joined_asset_path, asset_paths)

      FileUtils.mkdir_p(File.dirname(joined_asset_path))
      File.atomic_write(joined_asset_path, File.join(Rails.root, 'tmp')) { |cache| cache.write(join_asset_file_contents(asset_paths)) }

      # Set mtime to the latest of the combined files to allow for
      # consistent ETag without a shared filesystem.
      mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
      File.utime(mt, mt, joined_asset_path)
    end
  end
end

It stands to reason that the environment could be tweaked so that TMPDIR is assigned accordingly somewhere during the initialization, but that may alter behavior on a much larger scale.

The previous implementation in Rails 2.3.5 does not use atomic_write and does not suffer from this problem:

File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }

This has the unfortunate effect of presumably failing if the Rails application does not have its own tmp/ directory.

Comments and changes to this ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Attachments

Pages