bootsnap 1.4.1-java → 1.4.2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/CODEOWNERS +2 -0
- data/.github/probots.yml +2 -0
- data/.travis.yml +0 -3
- data/CHANGELOG.md +5 -0
- data/README.md +20 -2
- data/ext/bootsnap/bootsnap.c +6 -6
- data/lib/bootsnap/compile_cache.rb +12 -0
- data/lib/bootsnap/compile_cache/iseq.rb +2 -0
- data/lib/bootsnap/compile_cache/yaml.rb +9 -5
- data/lib/bootsnap/load_path_cache.rb +7 -0
- data/lib/bootsnap/load_path_cache/cache.rb +19 -1
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +13 -7
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +10 -0
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +10 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +9 -0
- data/lib/bootsnap/load_path_cache/path_scanner.rb +3 -1
- data/lib/bootsnap/load_path_cache/store.rb +5 -3
- data/lib/bootsnap/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4e355bad836c2faeacff354f3b026f03401171c4
|
4
|
+
data.tar.gz: 89cb1e0c808975099905f5ba9a7ee7cea58e9338
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc454087a30b54b6735ba8c8640f640261a5cd5f232fec2edf53438b868e74ef8e178bcd03e5a083f46188e5049776bbf4e3981cc7fea13927d24081e94f1cdf
|
7
|
+
data.tar.gz: 234c23ed91b2632db1bddae904a3d0c319016e84616172d9aaed0a666188ceeca9803d2f6d85f642a7917815fc094f352645938cd4b9f2fa9136658d137b2314
|
data/.github/CODEOWNERS
ADDED
data/.github/probots.yml
ADDED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -4,9 +4,14 @@ Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSup
|
|
4
4
|
to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
|
5
5
|
|
6
6
|
#### Performance
|
7
|
-
|
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,
|
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, and ~1% to `disable_trace`. This is fairly representative.
|
10
15
|
|
11
16
|
## Usage
|
12
17
|
|
@@ -28,6 +33,10 @@ Note that bootsnap writes to `tmp/cache`, and that directory *must* be writable.
|
|
28
33
|
boot if it is not. If this is unacceptable (e.g. you are running in a read-only container and
|
29
34
|
unwilling to mount in a writable tmpdir), you should remove this line or wrap it in a conditional.
|
30
35
|
|
36
|
+
**Note also that bootsnap will never clean up its own cache: this is left up to you. Depending on your
|
37
|
+
deployment strategy, you may need to periodically purge `tmp/cache/bootsnap*`. If you notice deploys
|
38
|
+
getting progressively slower, this is almost certainly the cause.**
|
39
|
+
|
31
40
|
It's technically possible to simply specify `gem 'bootsnap', require: 'bootsnap/setup'`, but it's
|
32
41
|
important to load Bootsnap as early as possible to get maximum performance improvement.
|
33
42
|
|
@@ -284,3 +293,12 @@ open /c/nope.bundle -> -1
|
|
284
293
|
```
|
285
294
|
# (nothing!)
|
286
295
|
```
|
296
|
+
|
297
|
+
## When not to use Bootsnap
|
298
|
+
|
299
|
+
*Alternative engines*: Bootsnap is pretty reliant on MRI features, and parts are disabled entirely on alternative ruby
|
300
|
+
engines.
|
301
|
+
|
302
|
+
*Non-local filesystems*: Bootsnap depends on `tmp/cache` (or whatever you set its cache directory
|
303
|
+
to) being on a relatively fast filesystem. If you put it on a network mount, bootsnap is very likely
|
304
|
+
to slow your application down quite a lot.
|
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -468,21 +468,21 @@ static int
|
|
468
468
|
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
|
469
469
|
{
|
470
470
|
char template[MAX_CACHEPATH_SIZE + 20];
|
471
|
-
char * dest;
|
472
471
|
char * tmp_path;
|
473
472
|
int fd, ret;
|
474
473
|
ssize_t nwrite;
|
475
474
|
|
476
|
-
|
477
|
-
strcat(
|
475
|
+
tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
476
|
+
strcat(tmp_path, ".tmp.XXXXXX");
|
478
477
|
|
479
|
-
|
480
|
-
fd =
|
478
|
+
// mkstemp modifies the template to be the actual created path
|
479
|
+
fd = mkstemp(tmp_path);
|
481
480
|
if (fd < 0) {
|
482
|
-
if (mkpath(
|
481
|
+
if (mkpath(tmp_path, 0775) < 0) {
|
483
482
|
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:mkpath";
|
484
483
|
return -1;
|
485
484
|
}
|
485
|
+
close(fd);
|
486
486
|
fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
|
487
487
|
if (fd < 0) {
|
488
488
|
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:open";
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module Bootsnap
|
2
2
|
module CompileCache
|
3
|
+
Error = Class.new(StandardError)
|
4
|
+
PermissionError = Class.new(Error)
|
5
|
+
|
3
6
|
def self.setup(cache_dir:, iseq:, yaml:)
|
4
7
|
if iseq
|
5
8
|
if supported?
|
@@ -20,6 +23,15 @@ module Bootsnap
|
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
26
|
+
def self.permission_error(path)
|
27
|
+
cpath = Bootsnap::CompileCache::ISeq.cache_dir
|
28
|
+
raise(
|
29
|
+
PermissionError,
|
30
|
+
"bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
|
31
|
+
"(or, less likely, doesn't have permisison to read '#{path}')",
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
23
35
|
def self.supported?
|
24
36
|
# only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), and >= 2.3.0
|
25
37
|
RUBY_ENGINE == 'ruby' &&
|
@@ -46,11 +46,15 @@ module Bootsnap
|
|
46
46
|
|
47
47
|
klass = class << ::YAML; self; end
|
48
48
|
klass.send(:define_method, :load_file) do |path|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
begin
|
50
|
+
Bootsnap::CompileCache::Native.fetch(
|
51
|
+
cache_dir,
|
52
|
+
path,
|
53
|
+
Bootsnap::CompileCache::YAML
|
54
|
+
)
|
55
|
+
rescue Errno::EACCES
|
56
|
+
Bootsnap::CompileCache.permission_error(path)
|
57
|
+
end
|
54
58
|
end
|
55
59
|
end
|
56
60
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bootsnap
|
2
4
|
module LoadPathCache
|
3
5
|
ReturnFalse = Class.new(StandardError)
|
@@ -7,6 +9,11 @@ module Bootsnap
|
|
7
9
|
DOT_SO = '.so'
|
8
10
|
SLASH = '/'
|
9
11
|
|
12
|
+
# If a NameError happens several levels deep, don't re-handle it
|
13
|
+
# all the way up the chain: mark it once and bubble it up without
|
14
|
+
# more retries.
|
15
|
+
ERROR_TAG_IVAR = :@__bootsnap_rescued
|
16
|
+
|
10
17
|
DL_EXTENSIONS = ::RbConfig::CONFIG
|
11
18
|
.values_at('DLEXT', 'DLEXT2')
|
12
19
|
.reject { |ext| !ext || ext.empty? }
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative('../explicit_require')
|
2
4
|
|
3
5
|
module Bootsnap
|
@@ -46,7 +48,7 @@ module Bootsnap
|
|
46
48
|
reinitialize if (@has_relative_paths && dir_changed?) || stale?
|
47
49
|
feature = feature.to_s
|
48
50
|
return feature if absolute_path?(feature)
|
49
|
-
return
|
51
|
+
return expand_path(feature) if feature.start_with?('./')
|
50
52
|
@mutex.synchronize do
|
51
53
|
x = search_index(feature)
|
52
54
|
return x if x
|
@@ -162,6 +164,10 @@ module Bootsnap
|
|
162
164
|
end
|
163
165
|
end
|
164
166
|
|
167
|
+
def expand_path(feature)
|
168
|
+
maybe_append_extension(File.expand_path(feature))
|
169
|
+
end
|
170
|
+
|
165
171
|
def stale?
|
166
172
|
@development_mode && @generated_at + AGE_THRESHOLD < now
|
167
173
|
end
|
@@ -174,10 +180,18 @@ module Bootsnap
|
|
174
180
|
def search_index(f)
|
175
181
|
try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f + DLEXT2) || try_index(f)
|
176
182
|
end
|
183
|
+
|
184
|
+
def maybe_append_extension(f)
|
185
|
+
try_ext(f + DOT_RB) || try_ext(f + DLEXT) || try_ext(f + DLEXT2) || f
|
186
|
+
end
|
177
187
|
else
|
178
188
|
def search_index(f)
|
179
189
|
try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f)
|
180
190
|
end
|
191
|
+
|
192
|
+
def maybe_append_extension(f)
|
193
|
+
try_ext(f + DOT_RB) || try_ext(f + DLEXT) || f
|
194
|
+
end
|
181
195
|
end
|
182
196
|
|
183
197
|
def try_index(f)
|
@@ -185,6 +199,10 @@ module Bootsnap
|
|
185
199
|
p + '/' + f
|
186
200
|
end
|
187
201
|
end
|
202
|
+
|
203
|
+
def try_ext(f)
|
204
|
+
f if File.exist?(f)
|
205
|
+
end
|
188
206
|
end
|
189
207
|
end
|
190
208
|
end
|
@@ -60,19 +60,22 @@ module Bootsnap
|
|
60
60
|
super
|
61
61
|
end
|
62
62
|
rescue NameError => e
|
63
|
+
raise(e) if e.instance_variable_defined?(Bootsnap::LoadPathCache::ERROR_TAG_IVAR)
|
64
|
+
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
65
|
+
|
63
66
|
# This function can end up called recursively, we only want to
|
64
67
|
# retry at the top-level.
|
65
|
-
raise if Thread.current[:without_bootsnap_retry]
|
68
|
+
raise(e) if Thread.current[:without_bootsnap_retry]
|
66
69
|
# If we already had cache disabled, there's no use retrying
|
67
|
-
raise if Thread.current[:without_bootsnap_cache]
|
70
|
+
raise(e) if Thread.current[:without_bootsnap_cache]
|
68
71
|
# NoMethodError is a NameError, but we only want to handle actual
|
69
72
|
# NameError instances.
|
70
|
-
raise unless e.class == NameError
|
73
|
+
raise(e) unless e.class == NameError
|
71
74
|
# We can only confidently handle cases when *this* constant fails
|
72
75
|
# to load, not other constants referred to by it.
|
73
|
-
raise unless e.name == const_name
|
76
|
+
raise(e) unless e.name == const_name
|
74
77
|
# If the constant was actually loaded, something else went wrong?
|
75
|
-
raise if from_mod.const_defined?(const_name)
|
78
|
+
raise(e) if from_mod.const_defined?(const_name)
|
76
79
|
CoreExt::ActiveSupport.without_bootsnap_cache { super }
|
77
80
|
end
|
78
81
|
|
@@ -80,9 +83,12 @@ module Bootsnap
|
|
80
83
|
# reiterate it with version polymorphism here...
|
81
84
|
def depend_on(*)
|
82
85
|
super
|
83
|
-
rescue LoadError
|
86
|
+
rescue LoadError => e
|
87
|
+
raise(e) if e.instance_variable_defined?(Bootsnap::LoadPathCache::ERROR_TAG_IVAR)
|
88
|
+
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
89
|
+
|
84
90
|
# If we already had cache disabled, there's no use retrying
|
85
|
-
raise if Thread.current[:without_bootsnap_cache]
|
91
|
+
raise(e) if Thread.current[:without_bootsnap_cache]
|
86
92
|
CoreExt::ActiveSupport.without_bootsnap_cache { super }
|
87
93
|
end
|
88
94
|
end
|
@@ -3,6 +3,7 @@ module Bootsnap
|
|
3
3
|
module CoreExt
|
4
4
|
def self.make_load_error(path)
|
5
5
|
err = LoadError.new("cannot load such file -- #{path}")
|
6
|
+
err.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
6
7
|
err.define_singleton_method(:path) { path }
|
7
8
|
err
|
8
9
|
end
|
@@ -30,6 +31,9 @@ module Kernel
|
|
30
31
|
end
|
31
32
|
|
32
33
|
raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
|
34
|
+
rescue LoadError => e
|
35
|
+
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
36
|
+
raise(e)
|
33
37
|
rescue Bootsnap::LoadPathCache::ReturnFalse
|
34
38
|
false
|
35
39
|
rescue Bootsnap::LoadPathCache::FallbackScan
|
@@ -56,6 +60,9 @@ module Kernel
|
|
56
60
|
end
|
57
61
|
|
58
62
|
raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
|
63
|
+
rescue LoadError => e
|
64
|
+
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
65
|
+
raise(e)
|
59
66
|
rescue Bootsnap::LoadPathCache::ReturnFalse
|
60
67
|
false
|
61
68
|
rescue Bootsnap::LoadPathCache::FallbackScan
|
@@ -74,6 +81,9 @@ class Module
|
|
74
81
|
# added to $LOADED_FEATURES and won't be able to hook that modification
|
75
82
|
# since it's done in C-land.
|
76
83
|
autoload_without_bootsnap(const, Bootsnap::LoadPathCache.load_path_cache.find(path) || path)
|
84
|
+
rescue LoadError => e
|
85
|
+
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
|
86
|
+
raise(e)
|
77
87
|
rescue Bootsnap::LoadPathCache::ReturnFalse
|
78
88
|
false
|
79
89
|
rescue Bootsnap::LoadPathCache::FallbackScan
|
@@ -4,4 +4,14 @@ class << $LOADED_FEATURES
|
|
4
4
|
Bootsnap::LoadPathCache.loaded_features_index.purge(key)
|
5
5
|
delete_without_bootsnap(key)
|
6
6
|
end
|
7
|
+
|
8
|
+
alias_method(:reject_without_bootsnap!, :reject!)
|
9
|
+
def reject!(&block)
|
10
|
+
backup = dup
|
11
|
+
|
12
|
+
# FIXME: if no block is passed we'd need to return a decorated iterator
|
13
|
+
reject_without_bootsnap!(&block)
|
14
|
+
|
15
|
+
Bootsnap::LoadPathCache.loaded_features_index.purge_multi(backup - self)
|
16
|
+
end
|
7
17
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bootsnap
|
2
4
|
module LoadPathCache
|
3
5
|
# LoadedFeaturesIndex partially mirrors an internal structure in ruby that
|
@@ -55,6 +57,13 @@ module Bootsnap
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
60
|
+
def purge_multi(features)
|
61
|
+
rejected_hashes = features.map(&:hash).to_set
|
62
|
+
@mutex.synchronize do
|
63
|
+
@lfi.reject! { |_, hash| rejected_hashes.include?(hash) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
58
67
|
def key?(feature)
|
59
68
|
@mutex.synchronize { @lfi.key?(feature) }
|
60
69
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative('../explicit_require')
|
2
4
|
|
3
5
|
module Bootsnap
|
@@ -11,7 +13,7 @@ module Bootsnap
|
|
11
13
|
BUNDLE_PATH = if Bootsnap.bundler?
|
12
14
|
(Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze
|
13
15
|
else
|
14
|
-
''
|
16
|
+
''
|
15
17
|
end
|
16
18
|
|
17
19
|
def self.call(path)
|
@@ -60,9 +60,11 @@ module Bootsnap
|
|
60
60
|
def load_data
|
61
61
|
@data = begin
|
62
62
|
MessagePack.load(File.binread(@store_path))
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
# handle malformed data due to upgrade incompatability
|
64
|
+
rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
|
65
|
+
{}
|
66
|
+
rescue ArgumentError => e
|
67
|
+
e.message =~ /negative array size/ ? {} : raise
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
data/lib/bootsnap/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bootsnap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.2
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,6 +101,8 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
+
- ".github/CODEOWNERS"
|
105
|
+
- ".github/probots.yml"
|
104
106
|
- ".gitignore"
|
105
107
|
- ".rubocop.yml"
|
106
108
|
- ".travis.yml"
|
@@ -165,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
167
|
version: '0'
|
166
168
|
requirements: []
|
167
169
|
rubyforge_project:
|
168
|
-
rubygems_version: 2.
|
170
|
+
rubygems_version: 2.4.8
|
169
171
|
signing_key:
|
170
172
|
specification_version: 4
|
171
173
|
summary: Boot large ruby/rails apps faster
|