bootsnap 1.4.1 → 1.10.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +189 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +67 -18
  5. data/exe/bootsnap +5 -0
  6. data/ext/bootsnap/bootsnap.c +319 -119
  7. data/ext/bootsnap/extconf.rb +22 -14
  8. data/lib/bootsnap/bundler.rb +2 -0
  9. data/lib/bootsnap/cli/worker_pool.rb +136 -0
  10. data/lib/bootsnap/cli.rb +281 -0
  11. data/lib/bootsnap/compile_cache/iseq.rb +65 -18
  12. data/lib/bootsnap/compile_cache/json.rb +88 -0
  13. data/lib/bootsnap/compile_cache/yaml.rb +332 -39
  14. data/lib/bootsnap/compile_cache.rb +35 -7
  15. data/lib/bootsnap/explicit_require.rb +5 -3
  16. data/lib/bootsnap/load_path_cache/cache.rb +83 -32
  17. data/lib/bootsnap/load_path_cache/change_observer.rb +6 -1
  18. data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +39 -47
  19. data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +12 -0
  20. data/lib/bootsnap/load_path_cache/loaded_features_index.rb +69 -26
  21. data/lib/bootsnap/load_path_cache/path.rb +8 -5
  22. data/lib/bootsnap/load_path_cache/path_scanner.rb +56 -29
  23. data/lib/bootsnap/load_path_cache/realpath_cache.rb +6 -5
  24. data/lib/bootsnap/load_path_cache/store.rb +49 -18
  25. data/lib/bootsnap/load_path_cache.rb +20 -32
  26. data/lib/bootsnap/setup.rb +3 -33
  27. data/lib/bootsnap/version.rb +3 -1
  28. data/lib/bootsnap.rb +126 -36
  29. metadata +15 -97
  30. data/.gitignore +0 -17
  31. data/.rubocop.yml +0 -20
  32. data/.travis.yml +0 -24
  33. data/CODE_OF_CONDUCT.md +0 -74
  34. data/CONTRIBUTING.md +0 -21
  35. data/Gemfile +0 -8
  36. data/README.jp.md +0 -231
  37. data/Rakefile +0 -12
  38. data/bin/ci +0 -10
  39. data/bin/console +0 -14
  40. data/bin/setup +0 -8
  41. data/bin/test-minimal-support +0 -7
  42. data/bin/testunit +0 -8
  43. data/bootsnap.gemspec +0 -45
  44. data/dev.yml +0 -10
  45. data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +0 -100
  46. data/shipit.rubygems.yml +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ecf22c8b6dc4b98a6f7ac7fe451d3bc7e2d29893c2bc8e9428b0143997e739a
4
- data.tar.gz: a57576a6e84953b44bf245e4e43e18a8a2a457a18fe5eb3688e066e9fe893c2c
3
+ metadata.gz: 3dfd9cfb2cde26fc31c82206b73f3940b4b46903a3fa73d0f3f1a4c4a90c6984
4
+ data.tar.gz: 6dd8acfb2676a2585eb320ed50ba0ddb9f9c847fb415bcda824c7ebff0cce752
5
5
  SHA512:
6
- metadata.gz: '005135654997ebaea3f0975bbc7be897b5d40eaaebec11573709ce83574f2131987441f103e795d45d7c83a4517ede8764e0a43c5e00cb53e84c8e498d9475e7'
7
- data.tar.gz: 85cc529d70ec6318d30f1edba6cbed627276ee3c7fb200096960a3cd08a953ad0f018601bf223d0a5216b981edd07c3fa9b54b3192daa8881e0625c2d4f7fa11
6
+ metadata.gz: 219a84be718e4ac5681621739b6f9400d77a16dee631bdc286adad87c0ddf99cc581da7281150add34075c08c59832df2a468524656d2f1ac36ac1613be051bb
7
+ data.tar.gz: 0e407a034fd081c9e506637e0997d0c2cc0eea312be798f6fd0d573173699f906f1b43a1f3d37ff64ee6efb2f9257228c6c4e7663b7337bfc49d99e5089fccb9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,192 @@
1
+ # Unreleased
2
+
3
+ # 1.10.3
4
+
5
+ * Fix Regexp and Date type support in YAML compile cache. (#400)
6
+
7
+ * Improve the YAML compile cache to support `UTF-8` symbols. (#398, #399)
8
+ [The default `MessagePack` symbol serializer assumes all symbols are ASCII](https://github.com/msgpack/msgpack-ruby/pull/211),
9
+ because of this, non-ASCII compatible symbol would be restored with `ASCII_8BIT` encoding (AKA `BINARY`).
10
+ Bootsnap now properly cache them in `UTF-8`.
11
+
12
+ Note that the above only apply for actual YAML symbols (e..g `--- :foo`).
13
+ The issue is still present for string keys parsed with `YAML.load_file(..., symbolize_names: true)`, that is a bug
14
+ in `msgpack` that will hopefully be solved soon, see: https://github.com/msgpack/msgpack-ruby/pull/246
15
+
16
+ * Entirely disable the YAML compile cache if `Encoding.default_internal` is set to an encoding not supported by `msgpack`. (#398)
17
+ `Psych` coerce strings to `Encoding.default_internal`, but `MessagePack` doesn't. So in this scenario we can't provide
18
+ YAML caching at all without returning the strings in the wrong encoding.
19
+ This never came up in practice but might as well be safe.
20
+
21
+ # 1.10.2
22
+
23
+ * Reduce the `Kernel.require` extra stack frames some more. Now bootsnap should only add one extra frame per `require` call.
24
+
25
+ * Better check `freeze` option support in JSON compile cache.
26
+ Previously `JSON.load_file(..., freeze: true)` would be cached even when the msgpack version is missing support for it.
27
+
28
+ # 1.10.1
29
+
30
+ * Fix `Kernel#autoload`'s fallback path always being executed.
31
+
32
+ * Consider `unlink` failing with `ENOENT` as a success.
33
+
34
+ # 1.10.0
35
+
36
+ * Delay requiring `FileUtils`. (#285)
37
+ `FileUtils` can be installed as a gem, so it's best to wait for bundler to have setup the load path before requiring it.
38
+
39
+ * Improve support of Psych 4. (#392)
40
+ Since `1.8.0`, `YAML.load_file` was no longer cached when Psych 4 was used. This is because `load_file` loads
41
+ in safe mode by default, so the Bootsnap cache could defeat that safety.
42
+ Now when precompiling YAML files, Bootsnap first try to parse them in safe mode, and if it can't fallback to unsafe mode,
43
+ and the cache contains a flag that records whether it was generated in safe mode or not.
44
+ `YAML.unsafe_load_file` will use safe caches just fine, but `YAML.load_file` will fallback to uncached YAML parsing
45
+ if the cache was generated using unsafe parsing.
46
+
47
+ * Minimize the Kernel.require extra stack frames. (#393)
48
+ This should reduce the noise generated by bootsnap on `LoadError`.
49
+
50
+ # 1.9.4
51
+
52
+ * Ignore absolute paths in the loaded feature index. (#385)
53
+ This fixes a compatibility issue with Zeitwerk when Zeitwerk is loaded before bootsnap. It also should
54
+ reduce the memory usage and improve load performance of Zeitwerk managed files.
55
+
56
+ * Automatically invalidate the load path cache whenever the Ruby version change. (#387)
57
+ This is to avoid issues in case the same installation path is re-used for subsequent ruby patch releases.
58
+
59
+ # 1.9.3
60
+
61
+ * Only disable the compile cache for source files impacted by [Ruby 3.0.3 [Bug 18250]](https://bugs.ruby-lang.org/issues/18250).
62
+ This should keep the performance loss to a minimum.
63
+
64
+ # 1.9.2
65
+
66
+ * Disable compile cache if [Ruby 3.0.3's ISeq cache bug](https://bugs.ruby-lang.org/issues/18250) is detected.
67
+ AKA `iseq.rb:13 to_binary: wrong argument type false (expected Symbol)`
68
+ * Fix `Kernel.load` behavior: before `load 'a'` would load `a.rb` (and other tried extensions) and wouldn't load `a` unless `development_mode: true`, now only `a` would be loaded and files with extensions wouldn't be.
69
+
70
+ # 1.9.1
71
+
72
+ * Removed a forgotten debug statement in JSON precompilation.
73
+
74
+ # 1.9.0
75
+
76
+ * Added a compilation cache for `JSON.load_file`. (#370)
77
+
78
+ # 1.8.1
79
+
80
+ * Fixed support for older Psych. (#369)
81
+
82
+ # 1.8.0
83
+
84
+ * Improve support for Psych 4. (#368)
85
+
86
+ # 1.7.7
87
+
88
+ * Fix `require_relative` in evaled code on latest ruby 3.1.0-dev. (#366)
89
+
90
+ # 1.7.6
91
+
92
+ * Fix reliance on `set` to be required.
93
+ * Fix `Encoding::UndefinedConversionError` error for Rails applications when precompiling cache. (#364)
94
+
95
+ # 1.7.5
96
+
97
+ * Handle a regression of Ruby 2.7.3 causing Bootsnap to call the deprecated `untaint` method. (#360)
98
+ * Gracefully handle read-only file system as well as other errors preventing to persist the load path cache. (#358)
99
+
100
+ # 1.7.4
101
+
102
+ * Stop raising errors when encountering various file system errors. The cache is now best effort,
103
+ if somehow it can't be saved, bootsnap will gracefully fallback to the original operation (e.g. `Kernel.require`).
104
+ (#353, #177, #262)
105
+
106
+ # 1.7.3
107
+
108
+ * Disable YAML precompilation when encountering YAML tags. (#351)
109
+
110
+ # 1.7.2
111
+
112
+ * Fix compatibility with msgpack < 1. (#349)
113
+
114
+ # 1.7.1
115
+
116
+ * Warn Ruby 2.5 users if they turn ISeq caching on. (#327, #244)
117
+ * Disable ISeq caching for the whole 2.5.x series again.
118
+ * Better handle hashing of Ruby strings. (#318)
119
+
120
+ # 1.7.0
121
+
122
+ * Fix detection of YAML files in gems.
123
+ * Adds an instrumentation API to monitor cache misses.
124
+ * Allow to control the behavior of `require 'bootsnap/setup'` using environment variables.
125
+ * Deprecate the `disable_trace` option.
126
+ * Deprecate the `ActiveSupport::Dependencies` (AKA Classic autoloader) integration. (#344)
127
+
128
+ # 1.6.0
129
+
130
+ * Fix a Ruby 2.7/3.0 issue with `YAML.load_file` keyword arguments. (#342)
131
+ * `bootsnap precompile` CLI use multiple processes to complete faster. (#341)
132
+ * `bootsnap precompile` CLI also precompile YAML files. (#340)
133
+ * Changed the load path cache directory from `$BOOTSNAP_CACHE_DIR/bootsnap-load-path-cache` to `$BOOTSNAP_CACHE_DIR/bootsnap/load-path-cache` for ease of use. (#334)
134
+ * Changed the compile cache directory from `$BOOTSNAP_CACHE_DIR/bootsnap-compile-cache` to `$BOOTSNAP_CACHE_DIR/bootsnap/compile-cache` for ease of use. (#334)
135
+
136
+ # 1.5.1
137
+
138
+ * Workaround a Ruby bug in InstructionSequence.compile_file. (#332)
139
+
140
+ # 1.5.0
141
+
142
+ * Add a command line to statically precompile the ISeq cache. (#326)
143
+
144
+ # 1.4.9
145
+
146
+ * [Windows support](https://github.com/Shopify/bootsnap/pull/319)
147
+ * [Fix potential crash](https://github.com/Shopify/bootsnap/pull/322)
148
+
149
+ # 1.4.8
150
+
151
+ * [Prevent FallbackScan from polluting exception cause](https://github.com/Shopify/bootsnap/pull/314)
152
+
153
+ # 1.4.7
154
+
155
+ * Various performance enhancements
156
+ * Fix race condition in heavy concurrent load scenarios that would cause bootsnap to raise
157
+
158
+ # 1.4.6
159
+
160
+ * Fix bug that was erroneously considering that files containing `.` in the names were being
161
+ required if a different file with the same name was already being required
162
+
163
+ Example:
164
+
165
+ require 'foo'
166
+ require 'foo.en'
167
+
168
+ Before bootsnap was considering `foo.en` to be the same file as `foo`
169
+
170
+ * Use glibc as part of the ruby_platform cache key
171
+
172
+ # 1.4.5
173
+
174
+ * MRI 2.7 support
175
+ * Fixed concurrency bugs
176
+
177
+ # 1.4.4
178
+
179
+ * Disable ISeq cache in `bootsnap/setup` by default in Ruby 2.5
180
+
181
+ # 1.4.3
182
+
183
+ * Fix some cache permissions and umask issues after switch to mkstemp
184
+
185
+ # 1.4.2
186
+
187
+ * Fix bug when removing features loaded by relative path from `$LOADED_FEATURES`
188
+ * Fix bug with propagation of `NameError` up from nested calls to `require`
189
+
1
190
  # 1.4.1
2
191
 
3
192
  * Don't register change observers to frozen objects.
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017 Shopify, Inc.
3
+ Copyright (c) 2017-present Shopify, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,12 +1,17 @@
1
- # Bootsnap [![Build Status](https://travis-ci.org/Shopify/bootsnap.svg?branch=master)](https://travis-ci.org/Shopify/bootsnap)
1
+ # Bootsnap [![Actions Status](https://github.com/Shopify/bootsnap/workflows/ci/badge.svg)](https://github.com/Shopify/bootsnap/actions)
2
2
 
3
- Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSupport` and `YAML`,
3
+ Bootsnap is a library that plugs into Ruby, with optional support for `YAML`,
4
4
  to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
5
5
 
6
6
  #### Performance
7
- - [Discourse](https://github.com/discourse/discourse) reports a boot time reduction of approximately 50%, from roughly 6 to 3 seconds on one machine;
7
+
8
+ - [Discourse](https://github.com/discourse/discourse) reports a boot time reduction of approximately
9
+ 50%, from roughly 6 to 3 seconds on one machine;
8
10
  - One of our smaller internal apps also sees a reduction of 50%, from 3.6 to 1.8 seconds;
9
- - The core Shopify platform -- a rather large monolithic application -- boots about 75% faster, dropping from around 25s to 6.5s.
11
+ - The core Shopify platform -- a rather large monolithic application -- boots about 75% faster,
12
+ dropping from around 25s to 6.5s.
13
+ * In Shopify core (a large app), about 25% of this gain can be attributed to `compile_cache_*`
14
+ features; 75% to path caching. This is fairly representative.
10
15
 
11
16
  ## Usage
12
17
 
@@ -24,14 +29,19 @@ If you are using Rails, add this to `config/boot.rb` immediately after `require
24
29
  require 'bootsnap/setup'
25
30
  ```
26
31
 
27
- Note that bootsnap writes to `tmp/cache`, and that directory *must* be writable. Rails will fail to
32
+ Note that bootsnap writes to `tmp/cache` (or the path specified by `ENV['BOOTSNAP_CACHE_DIR']`),
33
+ and that directory *must* be writable. Rails will fail to
28
34
  boot if it is not. If this is unacceptable (e.g. you are running in a read-only container and
29
35
  unwilling to mount in a writable tmpdir), you should remove this line or wrap it in a conditional.
30
36
 
37
+ **Note also that bootsnap will never clean up its own cache: this is left up to you. Depending on your
38
+ deployment strategy, you may need to periodically purge `tmp/cache/bootsnap*`. If you notice deploys
39
+ getting progressively slower, this is almost certainly the cause.**
40
+
31
41
  It's technically possible to simply specify `gem 'bootsnap', require: 'bootsnap/setup'`, but it's
32
42
  important to load Bootsnap as early as possible to get maximum performance improvement.
33
43
 
34
- You can see how this require works [here](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/setup.rb).
44
+ You can see how this require works [here](https://github.com/Shopify/bootsnap/blob/main/lib/bootsnap/setup.rb).
35
45
 
36
46
  If you are not using Rails, or if you are but want more control over things, add this to your
37
47
  application setup immediately after `require 'bundler/setup'` (i.e. as early as possible: the sooner
@@ -44,15 +54,11 @@ Bootsnap.setup(
44
54
  cache_dir: 'tmp/cache', # Path to your cache
45
55
  development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
46
56
  load_path_cache: true, # Optimize the LOAD_PATH with a cache
47
- autoload_paths_cache: true, # Optimize ActiveSupport autoloads with cache
48
- disable_trace: true, # Set `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
49
57
  compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
50
58
  compile_cache_yaml: true # Compile YAML into a cache
51
59
  )
52
60
  ```
53
61
 
54
- **Note that `disable_trace` will break debuggers and tracing.**
55
-
56
62
  **Protip:** You can replace `require 'bootsnap'` with `BootLib::Require.from_gem('bootsnap',
57
63
  'bootsnap')` using [this trick](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require). This
58
64
  will help optimize boot time further if you have an extremely large `$LOAD_PATH`.
@@ -62,12 +68,39 @@ speeds up the loading of individual source files, Spring keeps a copy of a pre-b
62
68
  on hand to completely skip parts of the boot process the next time it's needed. The two tools work
63
69
  well together, and are both included in a newly-generated Rails applications by default.
64
70
 
71
+ ### Environment variables
72
+
73
+ `require 'bootsnap/setup'` behavior can be changed using environment variables:
74
+
75
+ - `BOOTSNAP_CACHE_DIR` allows to define the cache location.
76
+ - `DISABLE_BOOTSNAP` allows to entirely disable bootsnap.
77
+ - `DISABLE_BOOTSNAP_LOAD_PATH_CACHE` allows to disable load path caching.
78
+ - `DISABLE_BOOTSNAP_COMPILE_CACHE` allows to disable ISeq and YAML caches.
79
+ - `BOOTSNAP_LOG` configure bootsnap to log all caches misses to STDERR.
80
+
65
81
  ### Environments
66
82
 
67
83
  All Bootsnap features are enabled in development, test, production, and all other environments according to the configuration in the setup. At Shopify, we use this gem safely in all environments without issue.
68
84
 
69
85
  If you would like to disable any feature for a certain environment, we suggest changing the configuration to take into account the appropriate ENV var or configuration according to your needs.
70
86
 
87
+ ### Instrumentation
88
+
89
+ Bootsnap cache misses can be monitored though a callback:
90
+
91
+ ```ruby
92
+ Bootsnap.instrumentation = ->(event, path) { puts "#{event} #{path}" }
93
+ ```
94
+
95
+ `event` is either `:miss` or `:stale`. You can also call `Bootsnap.log!` as a shortcut to
96
+ log all events to STDERR.
97
+
98
+ To turn instrumentation back off you can set it to nil:
99
+
100
+ ```ruby
101
+ Bootsnap.instrumentation = nil
102
+ ```
103
+
71
104
  ## How does this work?
72
105
 
73
106
  Bootsnap optimizes methods to cache results of expensive computations, and can be grouped
@@ -75,8 +108,6 @@ into two broad categories:
75
108
 
76
109
  * [Path Pre-Scanning](#path-pre-scanning)
77
110
  * `Kernel#require` and `Kernel#load` are modified to eliminate `$LOAD_PATH` scans.
78
- * `ActiveSupport::Dependencies.{autoloadable_module?,load_missing_constant,depend_on}` are
79
- overridden to eliminate scans of `ActiveSupport::Dependencies.autoload_paths`.
80
111
  * [Compilation caching](#compilation-caching)
81
112
  * `RubyVM::InstructionSequence.load_iseq` is implemented to cache the result of ruby bytecode
82
113
  compilation.
@@ -115,10 +146,6 @@ open y/foo.rb
115
146
  ...
116
147
  ```
117
148
 
118
- Exactly the same strategy is employed for methods that traverse
119
- `ActiveSupport::Dependencies.autoload_paths` if the `autoload_paths_cache` option is given to
120
- `Bootsnap.setup`.
121
-
122
149
  The following diagram flowcharts the overrides that make the `*_path_cache` features work.
123
150
 
124
151
  ![Flowchart explaining
@@ -134,7 +161,7 @@ The only directories considered "stable" are things under the Ruby install prefi
134
161
  "volatile".
135
162
 
136
163
  In addition to the [`Bootsnap::LoadPathCache::Cache`
137
- source](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/load_path_cache/cache.rb),
164
+ source](https://github.com/Shopify/bootsnap/blob/main/lib/bootsnap/load_path_cache/cache.rb),
138
165
  this diagram may help clarify how entry resolution works:
139
166
 
140
167
  ![How path searching works](https://cloud.githubusercontent.com/assets/3074765/25388270/670b5652-299b-11e7-87fb-975647f68981.png)
@@ -205,7 +232,7 @@ Bootsnap writes a cache file containing a 64 byte header followed by the cache c
205
232
  is a cache key including several fields:
206
233
 
207
234
  * `version`, hardcoded in bootsnap. Essentially a schema version;
208
- * `os_version`, A hash of the current kernel version (on macOS, BSD) or glibc version (on Linux);
235
+ * `ruby_platform`, A hash of `RUBY_PLATFORM` (e.g. x86_64-linux-gnu) variable and glibc version (on Linux) or OS version (`uname -v` on BSD, macOS)
209
236
  * `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
210
237
  * `ruby_revision`, the version of Ruby this was compiled with;
211
238
  * `size`, the size of the source file;
@@ -284,3 +311,25 @@ open /c/nope.bundle -> -1
284
311
  ```
285
312
  # (nothing!)
286
313
  ```
314
+
315
+ ## Precompilation
316
+
317
+ In development environments the bootsnap compilation cache is generated on the fly when source files are loaded.
318
+ But in production environments, such as docker images, you might need to precompile the cache.
319
+
320
+ To do so you can use the `bootsnap precompile` command.
321
+
322
+ Example:
323
+
324
+ ```bash
325
+ $ bundle exec bootsnap precompile --gemfile app/ lib/
326
+ ```
327
+
328
+ ## When not to use Bootsnap
329
+
330
+ *Alternative engines*: Bootsnap is pretty reliant on MRI features, and parts are disabled entirely on alternative ruby
331
+ engines.
332
+
333
+ *Non-local filesystems*: Bootsnap depends on `tmp/cache` (or whatever you set its cache directory
334
+ to) being on a relatively fast filesystem. If you put it on a network mount, bootsnap is very likely
335
+ to slow your application down quite a lot.
data/exe/bootsnap ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bootsnap/cli"
5
+ exit Bootsnap::CLI.new(ARGV).run