rails / propshaft

Deliver assets for Rails
MIT License
916 stars 97 forks source link

Maybe another race condition with webpack in development #192

Closed neontuna closed 1 month ago

neontuna commented 5 months ago

This may be a slight variation of https://github.com/rails/propshaft/issues/110 and I was hopeful that the 0.9.0 release would resolve but it hasn't

This is also hard to reproduce, although I currently have our app in a state locally where its constantly occurring so I can at least test possible solutions

We use Vuejs imported in application.js and compiled by webpack and jsbundling. I'll make a change to a Vue file, webpack will compile application.js, I'll reload the page and I'm met with a white screen and the following error in console

image Screenshot 2024-05-22 at 8 23 59 PM

The only way to resolve is to completely restart the rails server

So this seems like maybe that same chunking issue? I've tried digging into Propshaft itself to troubleshoot more but I'm not sure what I should be looking for - there's no errors from webpack cli or the rails console.

So far I've tried randomly bumping versions, from ruby 3.3.0 to 3.3.1. node 16 to 21. I also updated our webpack.config.js with the chunking options here https://github.com/rails/jsbundling-rails/blob/main/docs/switch_from_webpacker.md#optional-use-webpack-to-chunk-assets-so-that-it-works-with-sprockets

  output: {
    filename: "[name].js",
    chunkFilename: "[name]-[contenthash].digested.js",
    sourceMapFilename: "[file]-[fullhash].map",
    hashFunction: "sha256",
    hashDigestLength: 64,
    path: path.resolve(__dirname, '..', '..', 'app/assets/builds'),
  },
neontuna commented 5 months ago

I think I've completely disabled chunking in webpack but the issue persists. If it helps - here's the entire webpack.config.js

const path = require("path")
const webpack = require("webpack")
const { VueLoaderPlugin } = require('vue-loader')

const mode = process.env.NODE_ENV === 'production' ? 'production' : 'development';

module.exports = {
  mode,
  optimization: {
    moduleIds: 'deterministic',
  },
  entry: {
    application: "./app/javascript/packs/application.js",
    embed: "./app/javascript/packs/embed.js",
    fastboot: "./app/javascript/packs/fastboot.js",
    market_data_fastboot: "./app/javascript/packs/market_data_fastboot.js",
  },
  output: {
    filename: "[name].js",
    // chunkFilename: "[name]-[contenthash].digested.js",
    chunkFormat: false,
    sourceMapFilename: "[file]-[fullhash].map",
    // hashFunction: "sha256",
    // hashDigestLength: 64,
    path: path.resolve(__dirname, '..', '..', 'app/assets/builds'),
  },
  module: {
    rules: [
      {
        test: /\.mjs$/,
        include: /node_modules/,
        type: "javascript/auto"
      },
      {
        test: /\.vue(\.erb)?$/,
        use: [{
          loader: 'vue-loader'
        }]
      },
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      }
    ],
  },
  resolve: {
    extensions: ['.js', '.json', '.erb', '.vue'],
    alias: {
      '@': path.resolve(__dirname, '../../app/javascript/'),
    }
  },
  plugins: [
    // new webpack.optimize.LimitChunkCountPlugin({
    //   maxChunks: 1
    // }),
    new VueLoaderPlugin(),
  ]
}
neontuna commented 5 months ago

A little more here, I've narrowed down the issue to asset.fresh?(digest)

Essentially the digest coming in from env is differing from what Propshaft already has.

[2] pry(#<Propshaft::Server>)> asset.digest
=> "95d80481"
[3] pry(#<Propshaft::Server>)> path
=> "application.js"
[4] pry(#<Propshaft::Server>)> digest
=> "447086b2"
[5] pry(#<Propshaft::Server>)>

So asset.fresh?(digest) is returning nil, which gives the 404 error.

I'm not sure how to debug further - for now I'm just monkey patching the Propshaft::Server call to stop checking asset.fresh? before returning the asset (at least in development)

thedanbob commented 4 months ago

This is a long shot, but do you happen to be running a multi-process server in development? I was dealing with the same issue until just now. I've been running apps with Phusion Passenger in development (matching our production servers) which by default runs multiple processes, rather than multiple threads like Puma. The mutex added to fix #110 makes the asset server thread safe but only within a single process.

midnight-wonderer commented 3 months ago

Does the issue go away if you don't rely on Propshaft hash at all? (i.e., use .digested)

filename: "[name]-[contenthash:10].digested.js",
neontuna commented 2 months ago

This is a long shot, but do you happen to be running a multi-process server in development? I was dealing with the same issue until just now. I've been running apps with Phusion Passenger in development (matching our production servers) which by default runs multiple processes, rather than multiple threads like Puma. The mutex added to fix #110 makes the asset server thread safe but only within a single process.

Well I'm glad I decided to check back here - I guess I had notifications turned off in Github.

This fixed it! We're running Puma with the default workers at 2. Setting that to 0 in development and putting Puma into single process mode seems to have done the trick. Thank you!