Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canonicalize $BUNDLE_PATH in cache keys #343

Closed
wants to merge 2 commits into from
Closed

Conversation

casperisfine
Copy link
Contributor

Ref: heroku/heroku-buildpack-ruby#979

Heroku buildpacks build the application in one location
and then move it elsewhere. This cause all the paths to change,
hence all bootsnap cache keys to be invalidated.

By replacing $BUNDLE_PATH by a constant string in the cache keys,
we allow the bundler directory to be moved without flushing the cache.

Ideally we'd use a similar substitution for the "app root", but
I need to put more thoughts into it, as I'm not too sure how
best to infer it.

cc @schneems: If you have time to give this branch a try.

Ref: heroku/heroku-buildpack-ruby#979

Heroku buildpacks build the application in one location
and then move it elsewhere. This cause all the paths to change,
hence all bootsnap cache keys to be invalidated.

By replacing $BUNDLE_PATH by a constant string in the cache keys,
we allow the bundler directory to be moved without flushing the cache.

Ideally we'd use a similar substitution for the "app root", but
I need to put more thoughts into it, as I'm not too sure how
best to infer it.
@casperisfine
Copy link
Contributor Author

So after implementing this, I wonder if we could do the same with app paths, e.g. record Dir.pwd when bootsnap is loaded, and assume it's the app root, and canonicalize paths with this. I think it would work.

@schneems
Copy link

😍

I gave it a shot, and it looks like there might be a bug or two:

$ heroku run bash
Running bash on ⬢ secure-river-98214... ⣻ connecting, run.8519 (Free)
~ $ du -hs tmp/cache
14M	tmp/cache
~ $ time rails runner 'puts "hello world"'
/app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require': cannot load such file -- /tmp/build_620a6660/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/gem_version (LoadError)
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `block in require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:299:in `load_dependency'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `require'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:53:in `require_relative'
	from /tmp/build_620a6660/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/version.rb:3:in `<main>'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `block in require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:299:in `load_dependency'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `require'
	from /tmp/build_620a6660/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support.rb:28:in `<main>'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `block in require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:299:in `load_dependency'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `require'
	from /tmp/build_620a6660/vendor/bundle/ruby/3.0.0/gems/railties-6.1.1/lib/rails/command.rb:3:in `<main>'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `block in require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:299:in `load_dependency'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `require'
	from /tmp/build_620a6660/vendor/bundle/ruby/3.0.0/gems/railties-6.1.1/lib/rails/commands.rb:3:in `<main>'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	from /app/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-f9d95014ba2f/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `block in require'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:299:in `load_dependency'
	from /app/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `require'
	from /app/bin/rails:5:in `<main>'
real	0m1.795s
user	0m1.264s
sys	0m0.184s
~ $ du -hs tmp/cache
15M	tmp/cache
~ $

Note that it's still looking for /tmp/build_620a6660 dir even though it's now inside of /app. Here's my code that is pointing at your branch: https://github.com/schneems/pub_bootsnap_dir_example

@casperisfine
Copy link
Contributor Author

Hum, that's looks like a problem with the LOAD_PATH cache, which I haven't touched. Thanks for trying it out, I'll experiment some more as soon as I find time for it.

@casperisfine
Copy link
Contributor Author

It's weird, I can't repro locally:

$ \rm -rf tmp/cache/bootsnap/
$ DISABLE_SPRING=1 bundle exec bin/rails runner 'p 1'
1
$ du -sh tmp/cache/bootsnap/
 10M	tmp/cache/bootsnap/
$ cd ..
$ mv pub_bootsnap_dir_example/ /tmp/
pub_bootsnap_dir_example/ -> /tmp/pub_bootsnap_dir_example/
$ cd /tmp/pub_bootsnap_dir_example/
chruby: unknown Ruby: 3.0.0
$ chruby_use /opt/rubies/3.0.0-pshopify2/
$ DISABLE_SPRING=1 bundle exec bin/rails runner 'p 1'
1
$ du -sh tmp/cache/bootsnap/
 10M	tmp/cache/bootsnap/

From afar it seem to be working as intended.

@casperisfine
Copy link
Contributor Author

So I figured why it would repro locally. The problem occur when the BUNDLE_PATH is inside the app root bundle install --path vendor/bundle does it.

@casperisfine
Copy link
Contributor Author

It seems to break on require_relative

/Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require': cannot load such file -- /private/tmp/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/gem_version (LoadError)
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `block in require'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:299:in `load_dependency'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `require'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:54:in `require_relative'
	from /private/tmp/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/version.rb:3:in `<main>'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/bundler/gems/bootsnap-153616dea44e/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `block in require'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:299:in `load_dependency'
	from /Users/byroot/src/github.com/schneems/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support/dependencies.rb:332:in `require'
	from /private/tmp/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support.rb:28:in `<main>'

require_relative "gem_version"

The backtrace suggest that /private/tmp/pub_bootsnap_dir_example/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.1/lib/active_support.rb:28 is executed (even though it's the old location). My current theory is that ISeq caches the __FILE__ property. I'll try to confirm that.

@casperisfine
Copy link
Contributor Author

Looks like it indeed:

$ strings /tmp/dump.isq 
YARB
x86_64-darwin19
<main>
/tmp/iseq.rb
)/private/tmp/iseq.rb

@casperisfine
Copy link
Contributor Author

Yep:

File.write('/tmp/iseq.rb', 'p __FILE__')

File.write('/tmp/a.rb', 'p :a')
File.write('/tmp/b.rb', 'p :b')

$iseq = RubyVM::InstructionSequence.compile_file('/tmp/iseq.rb')

class RubyVM::InstructionSequence
  def self.load_iseq(feature)
    $iseq
  end
end

require '/tmp/a.rb'
require '/tmp/b.rb'
$ ruby /tmp/iseq_debug.rb
"/tmp/iseq.rb"
"/tmp/iseq.rb"

I'll dig in Ruby to see what's doable, but this might require an MRI change, in which case it wouldn't be doable until the next minor release, unless we find a hack in the meantime.

@casperisfine
Copy link
Contributor Author

Yeah, so I'm afraid ISeq#path and ISeq#absolute_path can't be modified. On the Ruby layer there is no setter, and in C-land the rb_iseq_t struct isn't exposed in headers, so I doubt we could change it in a reliable way.

I'll start a PR/ticket upstream to get ISeq#path= and ISeq#absolute_path=, but as said previously it would mean Ruby 3.1 at best.

@casperisfine
Copy link
Contributor Author

Ok, so that's going to be tricky. I did a quick & dirty patch to overwrite the ISeq location object: Shopify/ruby@192f5b4

With the following test script:

system('mkdir', '-p', '/tmp/build', '/tmp/app')
File.write('/tmp/app/a.rb', 'p ["app/a", __FILE__]')
File.write('/tmp/app/b.rb', 'p ["app/b", __FILE__]')
File.write('/tmp/build/a.rb', 'p ["build/a", __FILE__]; require_relative "b"')

$iseq = RubyVM::InstructionSequence.compile_file('/tmp/build/a.rb')

class RubyVM::InstructionSequence
  def self.load_iseq(feature)
    if feature == "/tmp/app/a.rb"
      $iseq
    end
  end
end

require '/tmp/app/a.rb'

Which partly works:

$ ./ruby --disable-gems --disable-did-you-mean /tmp/iseq_debug.rb 
["build/a", "/tmp/build/a.rb"]
["app/b", "/private/tmp/app/b.rb"]

The iseq cache is loaded correctly, and the require_relative works. So in some way it's "functional".

However you can notice that the __FILE__ is still evaluating to the build path. So if you were to read files based on it, it would fail.

From what I gathered __FILE__ is eagerly replaced by a "literal" string by the parser at "compile" time. So For all this to work, I'd need to figure out a way to replace that "literal" by something that would lookup the ISeq#absolute_path.

Also that patch is super hacky, as it changes the location in place. A proper patch would need to work on a copy or something. Or ideally the ISeq#path should just not be considered when evaluating ISeq returned by load_iseq.

I'll chat a bit with our resident MRI people, see what they thing about it, but I doubt it will be an easy fix, so I wouldn't hold my breath.

Ultimately that raise the question of the usefulness of bootsnap on Heroku. It might be worth disabling it entirely there :/

@casperisfine
Copy link
Contributor Author

From what I gathered __FILE__ is eagerly replaced by a "literal" string by the parser at "compile" time.

system('mkdir', '-p', '/tmp/build', '/tmp/app')
File.write('/tmp/app/a.rb', 'p ["app/a", __FILE__, __dir__]')
File.write('/tmp/app/b.rb', 'p ["app/b", __FILE__, __dir__]')
File.write('/tmp/build/a.rb', 'p ["build/a", __FILE__, __dir__]; require_relative "b"')

$iseq = RubyVM::InstructionSequence.compile_file('/tmp/build/a.rb')

class RubyVM::InstructionSequence
  def self.load_iseq(feature)
    if feature == "/tmp/app/a.rb"
      $iseq
    end
  end
end

require '/tmp/app/a.rb'
["build/a", "/tmp/build/a.rb", "/private/tmp/app"]
["app/b", "/private/tmp/app/b.rb", "/private/tmp/app"]

So __dir__ works fine because it is an actual method. The way out of this is likely to make __FILE__ works the same way.

@schneems
Copy link

Wow. Thanks for digging in. Previously we talked about storing the cache files in the same location as the gem. Would this new information change the viability of that technique?

@casperisfine
Copy link
Contributor Author

Would this new information change the viability of that technique?

Yep, since Heroku use $APP/vendor/bundle, it would change location anyway, so it would be just as invalidated / broken.

So unless I'm missing something, I'm afraid there no way to make Bootsnap usable on Heroku. The best thing we could do for now I think, would be to automatically turn it off. Is there some kind of env var or something we could use to detect we're running on heroku?

@schneems
Copy link

So unless I'm missing something, I'm afraid there no way to make Bootsnap usable on Heroku. The best thing we could do for now I think, would be to automatically turn it off. Is there some kind of env var or something we could use to detect we're running on heroku?

There's still some benefit for running on deploy as there's several commands that are executed that will use the same cache. (We run rake assets:precompile then rails runner to gather config).

But having the cache files stored in the app's "slug" (tar file copy of the app generated after build time) it's just adding extra space. I could remove them after the build.

I think instead of you turning it off for Heroku, I can manage these concerns where they are an issue. Eventually (someday?) we'll be able to build and run on the same path and I don't want to have to handle toggling this off on bootsnap's side and then warning people they need to upgrade.

Is there a way to disable cache generation for bootsnap at runtime similar to DISABLE_SPRING ? I could generate and use the cache at build time, but then disable generating these files at runtime as it's actually adding to boot time.

@casperisfine
Copy link
Contributor Author

There's still some benefit for running on deploy as there's several commands that are executed that will use the same cache

Good point.

I could remove them after the build.

That would make sense I think. Right now they're actually slowing things down.

Is there a way to disable cache generation for bootsnap at runtime similar to DISABLE_SPRING

Not yet, but that would be trivial to add.

@casperisfine
Copy link
Contributor Author

Actually, now that I think about it, you only want to disable the CompileCache. The LoadPathCache should still bring you a major speedup.

@schneems
Copy link

Actually, now that I think about it, you only want to disable the CompileCache. The LoadPathCache should still bring you a major speedup.

I can test this on CodeTriage. Looking now.

@schneems
Copy link

$ heroku run bash -a issuetriage
~ $ mv tmp/cache tmp/cache-old
~ $ mkdir -p tmp/cache
~ $ cp tmp/cache-old/bootsnap-load-path-cache tmp/cache/bootsnap-load-path-cache

With build load-path-cache:

~ $ time rails runner "puts 'lol'"
lol

real  0m7.087s
user  0m5.908s
sys 0m0.896s

With that cache removed:

~ $ rm -rf tmp/cache
~ $ time rails runner "puts 'lol'"
lol

real  0m7.831s
user  0m5.836s
sys 0m1.764s

It would be better to have more than one sample, but this first experiment does seem to show that the load path cache from build time helps.

@casperisfine
Copy link
Contributor Author

he load path cache from build time helps.

Yes and no. Your load path cache from build time will be fully invalidated anyway, since your entire $LOAD_PATH is different.

But the nice thing with the load path cache, is that even on a cold cache (e.g. just removed), it still does speedup boot. So I think from the ruby-buildpack POV, the best course of action for now is:

  • Entirely delete tmp/cache/bootsnap* once you move to /app
  • Set some DISABLE_BOOTSNAP_COMPILE_CACHE var (doesn't exist yet, but I'm open to introduce it).

@schneems
Copy link

DISABLE_BOOTSNAP_COMPILE_CACHE sounds good.

@casperisfine
Copy link
Contributor Author

Let me know if that does it for you: b941bad

Also I'll try to followup upstream on that ISeq path issue. I think it wouldn't be that hard to fix, but of course at best it will be available in a year.

@schneems
Copy link

That logic looks good for me. DISABLE_BOOTSNAP_COMPILE_CACHE=1 looks like it would do the trick at runtime. Maybe some tests to ensure this doesn't regress.

@casperisfine
Copy link
Contributor Author

Maybe some tests to ensure this doesn't regress.

Yeah I got lazy here. I'll make sure to add some on Monday. Probably cut a new release as well.

@schneems
Copy link

😉 I figured. I like to prototype and play around before cementing with tests.

Also FWIW I talked with a co-worker today about this issue. It seems that work has been done to move the deploy infrastructure out of /app at deploy time. I'm going to try to see if I can symlink /app to the /tmp/<sha>_build dir. I don't know if I'll be able to do that without breaking other stuff, but it's something I'll queue up to look into. If that works out then we might not need to do this cache disabling dance (but I still think it's a nice option to have if it ends up not working out).

@casperisfine
Copy link
Contributor Author

I'm going to try to see if I can symlink /app to the /tmp/<sha>_build dir.

That would be a very good workaround yes. However keep in mind that Ruby loves to call realpath, so it means customers backtraces will change from /app, to /tmp/<sha>_build. No idea wether that's a problem for you.

Also I opened an issue upstream: https://bugs.ruby-lang.org/issues/17593

@casperisfine
Copy link
Contributor Author

I'll make sure to add some on Monday. Probably cut a new release as well.

Done. 1.7.0 is out. Also include a new instrumentation API to debug cache misses.

@schneems
Copy link

schneems commented Feb 2, 2021

Awesome, thanks!

FWIW we're looking into launching a change to building in /app. It's not for-sure going to happen, but I've got my fingers crossed. Also there would be a slow rollout with an opt-in period (using heroku feature flags). Still, I'm optimistic.

@casperisfine
Copy link
Contributor Author

Blocked by an upstream issue, no point keeping this open right now. I'll resume the work if the necessary changes are made in Ruby.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants