Asked  7 Months ago    Answers:  5   Viewed   50 times

The default Rails app installed by rails new has config.assets.compile = false in production.

And the ordinary way to do things is to run rake assets:precompile before deploying your app, to make sure all asset pipeline assets are compiled.

So what happens if I set config.assets.compile = true in production?

I wont' need to run precompile anymore. What I believe will happen is the first time an asset is requested, it will be compiled. This will be a performance hit that first time (and it means you generally need a js runtime in production to do it). But other than these downsides, after the asset was lazily compiled, I think all subsequent access to that asset will have no performance hit, the app's performance will be exactly the same as with precompiled assets after this initial first-hit lazy compilation. is this true?

Is there anything I'm missing? Any other reasons not to set config.assets.compile = true in production? If I've got a JS runtime in production, and am willing to take the tradeoff of degraded performance for the first access of an asset, in return for not having to run precompile, does this make sense?

 Answers

12

I wrote that bit of the guide.

You definitely do not want to live compile in production.

When you have compile on, this is what happens:

Every request for a file in /assets is passed to Sprockets. On the first request for each and every asset it is compiled and cached in whatever Rails is using for cache (usually the filesystem).

On subsequent requests Sprockets receives the request and has to look up the fingerprinted filename, check that the file (image) or files(css and js) that make up the asset were not modified, and then if there is a cached version serve that.

That is everything in the assets folder and in any vendor/assets folders used by plugins.

That is a lot of overhead as, to be honest, the code is not optimized for speed.

This will have an impact on how fast asset go over the wire to the client, and will negatively impact the page load times of your site.

Compare with the default:

When assets are precompiled and compile is off, assets are compiled and fingerprinted to the public/assets. Sprockets returns a mapping table of the plain to fingerprinted filenames to Rails, and Rails writes this to the filesystem. The manifest file (YML in Rails 3 or JSON with a randomised name in Rails 4) is loaded into Memory by Rails at startup and cached for use by the asset helper methods.

This makes the generation of pages with the correct fingerprinted assets very fast, and the serving of the files themselves are web-server-from-the-filesystem fast. Both dramatically faster than live compiling.

To get the maximum advantage of the pipeline and fingerprinting, you need to set far-future headers on your web server, and enable gzip compression for js and css files. Sprockets writes gzipped versions of assets which you can set your server to use, removing the need for it to do so for each request.

This get assets out to the client as fast as possible, and in the smallest size possible, speeding up client-side display of the pages, and reducing (with far-future header) requests.

So if you are live compiling it is:

  1. Very slow
  2. Lacks compression
  3. Will impact render time of pages

Versus

  1. As fast as possible
  2. Compressed
  3. Remove compression overheard from server (optionally).
  4. Minimize render time of pages.

Edit: (Answer to follow up comment)

The pipeline could be changed to precompile on the first request but there are some major roadblocks to doing so. The first is that there has to be a lookup table for fingerprinted names or the helper methods are too slow. Under a compile-on-demand senario there would need to be some way to append to the lookup table as each new asset is compiled or requested.

Also, someone would have to pay the price of slow asset delivery for an unknown period of time until all the assets are compiled and in place.

The default, where the price of compiling everything is paid off-line at one time, does not impact public visitors and ensures that everything works before things go live.

The deal-breaker is that it adds a lot of complexity to production systems.

[Edit, June 2015] If you are reading this because you are looking for a solution for slow compile times during a deploy, then you could consider precompiling the assets locally. Information on this is in the asset pipeline guide. This allows you to precompile locally only when there is a change, commit that, and then have a fast deploy with no precompile stage.

Tuesday, June 1, 2021
 
Avicinnian
answered 7 Months ago
38

We had this problem last week - the problem is that your assets will be compiled to have MD5 hashes on them, whilst your standard CSS will still be looking for their "standard" names. This is a problem with images & fonts.

@font-face {
    font-family: 'akagi';
    src: asset_url('fonts/akagi-th-webfont.eot');
    src: asset_url('fonts/akagi-th-webfont.eot?#iefix') format('embedded-opentype'),
         asset_url('fonts/akagi-th-webfont.woff') format('woff'),
         asset_url('fonts/akagi-th-webfont.ttf') format('truetype'),
         asset_url('fonts/akagi-th-webfont.svg#akagithin') format('svg');
    font-weight: 300;
    font-style: normal;
}

This is an example of how you should use scss files to load assets dynamically. These files are compiled (either before push or during init) into your .css files, all with their assets correctly synced.

We had a similar problem to you with Heroku, and managed to get it working by putting our files into /stylesheets/layout/fonts.css.scss and then calling

@import '/layout/fonts';

We also called our application.css -> application.css.scss to support the @import function

Thursday, June 24, 2021
 
madphp
answered 6 Months ago
55

Try adding the following to development.rb:

config.serve_static_assets = false

...and then clearing your browser cache (update based on comments)

The static assets refer to precompiled assets in public/assets, which is where rake assets:precompile puts them.

What's happening is that anything that exists in public/assets will override anything in app/assets if you are serving them. So public/assets/application.js is being loaded when the js tag is intending to identifiy app/assets/application.js.

Monday, June 28, 2021
 
THEK
answered 6 Months ago
41

I found this in edgerails documentation: http://edgeguides.rubyonrails.org/asset_pipeline.html#css-and-sass

2.3.2 CSS and Sass

When using the asset pipeline, paths to assets must be re-written and sass-rails provides -url and -path helpers (hyphenated in Sass, underscored in Ruby) for the following asset classes: image, font, video, audio, JavaScript and stylesheet.

  • image-url("rails.png") becomes url(/assets/rails.png)
  • image-path("rails.png") becomes "/assets/rails.png"

The more generic form can also be used but the asset path and class must both be specified:

  • asset-url("rails.png", image) becomes url(/assets/rails.png)
  • asset-path("rails.png", image) becomes "/assets/rails.png"
Saturday, August 14, 2021
 
DHranger
answered 4 Months ago
39

Low memory is the most likely cause that I have been able to come up with. I will post here if I can prove it.

Saturday, October 30, 2021
 
sholsinger
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share