mini_racer_rs 1.0.3 → 1.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfd3987a3aebfd95013e67e552b2041b991fd7b0da4933f98ae92335f7caa3a4
4
- data.tar.gz: 51ccdf38b39ab3f33f771f3cf7fdbd7422bda16037e8bc026cfd64dc6fdfbdae
3
+ metadata.gz: c1f337ddf902559ced22f49783b4f541c2ead54c00d1a72ee7a354e1e0b831fc
4
+ data.tar.gz: 45fe66cb3b1fd6af17aa4da11fa689d387a586eaab1ffd3fe0349fddc90bbec8
5
5
  SHA512:
6
- metadata.gz: ca5321d48ea8913f86d8966b6f475857d11b962c1f109e15f049fed590a517233f2cc3772fad99a9ff189f110995d3b2a0268f54e9f90aa48fcdd3cc1ac2b916
7
- data.tar.gz: 3c2a6c804148cfd0bdf198d5a63ef4b440d63c667d7468ab646e8b85b277eb15ec0fa26f5d9877f426c01427e8f841723cf31d00bec0ee546881c895d373c61b
6
+ metadata.gz: 924bb67c0d847c12570f983e86594d3ea432f76f206f73d725c3908b272634b5252699652a46231655e714ece2822942c474832b9618681d3bea3c120f7138b4
7
+ data.tar.gz: 8ead9bef3dac6010fe255d66e009583a7d1e2e8dc2c07aa655dbf21a3f9cda6b0416a74d676a28ff18e916a60f6c644d54e8265d3abe65c0927f5dc6870bcf89
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.5](https://github.com/andymai/mini_racer_rs/compare/v1.0.4...v1.0.5) (2026-10-19)
4
+
5
+
6
+ ### Performance
7
+
8
+ * **encode:** send Ruby strings to V8 in the form V8 stores them ([#93](https://github.com/andymai/mini_racer_rs/issues/93)) ([62f9c85](https://github.com/andymai/mini_racer_rs/commit/62f9c851917cb0ce7d760b5fc545aa014df0a53b))
9
+
10
+ ## [1.0.4](https://github.com/andymai/mini_racer_rs/compare/v1.0.3...v1.0.4) (2026-10-19)
11
+
12
+
13
+ ### Performance
14
+
15
+ * **engine:** share a large source from its first evaluation ([#91](https://github.com/andymai/mini_racer_rs/issues/91)) ([31425e1](https://github.com/andymai/mini_racer_rs/commit/31425e1fc72083fd1c6564d5f50d30b8e2d746aa))
16
+
3
17
  ## [1.0.3](https://github.com/andymai/mini_racer_rs/compare/v1.0.2...v1.0.3) (2026-10-18)
4
18
 
5
19
 
data/COMPATIBILITY.md CHANGED
@@ -182,7 +182,7 @@ Approved 2026-10-16.
182
182
 
183
183
  ### `behavior:shared-source-heap-stats@ruby`
184
184
 
185
- `Context#heap_stats` can report large source text differently from mini_racer 0.22.1. When a context evaluates a source of 1 MiB or more that the process has evaluated before under the same file name and startup snapshot, mini_racer_rs gives V8 one copy shared by every such context and held outside V8's heap. It reports that source in `:external_memory` and omits it from `:used_heap_size`. V8 counts the shared source by its length in characters, so two-byte text reports half its bytes. mini_racer 0.22.1 reports `:external_memory` as 0 and counts each context's own copy in `:used_heap_size`. JavaScript sees the same source text in both gems. Contexts using `max_memory:` and sources that are not valid UTF-8 retain individual copies. Setting `MINI_RACER_RS_CODE_CACHE=off` before creating the first context disables sharing and makes `heap_stats` count every source as mini_racer does.
185
+ `Context#heap_stats` reports shared sources differently from mini_racer 0.22.1: when a context evaluates a source of 1 MiB or more, mini_racer_rs includes it in `:external_memory` and excludes it from `:used_heap_size`. mini_racer_rs gives V8 one copy held outside V8's heap, shared starting with the first evaluation by every context using the same source, file name, and startup snapshot. The process shares at most 8 such sources and 256 MiB of them. Past that bound, a context evaluating another large source keeps its own copy, and its `heap_stats` count that source as mini_racer's do. V8 counts shared sources in `:external_memory` by character length, so two-byte text reports half its bytes. mini_racer 0.22.1 reports `:external_memory` as 0 and counts each context's own copy in `:used_heap_size`. JavaScript sees the same source text in both gems. Contexts created with `max_memory:` and sources that are not valid UTF-8 keep their own copies. Setting `MINI_RACER_RS_CODE_CACHE=off` before the first context prevents source sharing and makes `heap_stats` count every source as mini_racer does.
186
186
 
187
187
  Approved 2026-10-18.
188
188
 
data/Cargo.lock CHANGED
@@ -458,7 +458,7 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
458
458
 
459
459
  [[package]]
460
460
  name = "mini_racer_engine"
461
- version = "1.0.3"
461
+ version = "1.0.5"
462
462
  dependencies = [
463
463
  "deno_core_icudata",
464
464
  "libc",
@@ -470,7 +470,7 @@ dependencies = [
470
470
 
471
471
  [[package]]
472
472
  name = "mini_racer_rs"
473
- version = "1.0.3"
473
+ version = "1.0.5"
474
474
  dependencies = [
475
475
  "magnus",
476
476
  "mini_racer_engine",
@@ -481,7 +481,7 @@ dependencies = [
481
481
 
482
482
  [[package]]
483
483
  name = "mini_racer_sync"
484
- version = "1.0.3"
484
+ version = "1.0.5"
485
485
  dependencies = [
486
486
  "loom",
487
487
  "thiserror",
@@ -489,7 +489,7 @@ dependencies = [
489
489
 
490
490
  [[package]]
491
491
  name = "mini_racer_wire"
492
- version = "1.0.3"
492
+ version = "1.0.5"
493
493
  dependencies = [
494
494
  "proptest",
495
495
  "thiserror",
data/Cargo.toml CHANGED
@@ -14,7 +14,7 @@ license = "MIT"
14
14
  publish = false
15
15
  repository = "https://github.com/andymai/mini_racer_rs"
16
16
  rust-version = "1.91"
17
- version = "1.0.3"
17
+ version = "1.0.5"
18
18
 
19
19
  [workspace.dependencies]
20
20
  deno_core_icudata = "=0.78.0"
data/README.md CHANGED
@@ -21,10 +21,10 @@ mini_racer_rs implements the `MiniRacer` Ruby API from mini_racer 0.22.1, with f
21
21
 
22
22
  ## Highlights
23
23
 
24
- - **4 to 5 times faster Ruby round trips.** An eval, call, or callback round trip takes 1.3 to 1.6 µs, against 6.1 to 6.6 µs in mini_racer. A call on a context with `timeout:` takes 1.6 µs instead of 21.1 µs because one watchdog serves the process.
25
- - **1.9 times faster Babel transforms.** A transform through ExecJS takes 5.3 ms instead of 10.0 ms. Babel itself takes 5.3 ms inside V8 15.2, against 10.3 ms inside V8 13.6. Terser and React rendering take about the same time in both gems.
26
- - **4.6 times faster production bundle loads.** Scripts of 1 MB or more compile from a process-wide V8 code cache. A 44 MB production server rendering bundle loads into a new context in 215 ms against 983 ms. The 8.2 MB MUI bundle loads and renders once in 121 ms against 167 ms, and startup plus Babel takes 14.8 ms against 44.8 ms.
27
- - **Less memory for loaded and abandoned contexts.** A context holding the 44 MB bundle takes 136 MB against 301 MB, and one holding the MUI bundle takes 56 MB against 110 MB. When 20,000 contexts are left to Ruby's garbage collector, resident memory peaks at 102 MB against 2,916 MB.
24
+ - **JavaScript round trips are 5 to 9 times faster.** An eval takes 1.09 µs against 6.91 µs, a call takes 0.72 µs against 6.51 µs, and a callback takes 1.12 µs against 6.12 µs. A call on a context with `timeout:` takes 0.76 µs instead of 20.48 µs because one watchdog serves the process.
25
+ - **A Babel transform through ExecJS is 1.95x faster.** It takes 5.15 ms instead of 10.05 ms. Babel itself takes 5.3 ms inside V8 15.2, against 10.3 ms inside V8 13.6, while Terser and React rendering take about the same time in both gems.
26
+ - **A 44 MB production server rendering bundle loads 4.6x faster.** It loads into a new context in 215 ms against 983 ms, measured at commit `2513a48`, and scripts of 1 MB or more compile from a process-wide V8 code cache. At commit `220bfe7`, the 8.2 MB MUI bundle loads and renders once in 105 ms against 167 ms, while startup plus Babel takes 13.3 ms against 44.4 ms.
27
+ - **A context holding the MUI bundle uses 44 MB instead of 110 MB.** At commit `220bfe7`, a context holding Babel uses 15.2 MB against 28.5 MB, and contexts loading the same large script read one shared copy of its source. A context holding the 44 MB production bundle used 136 MB against 301 MB at commit `2513a48`, before sources were shared. When 20,000 contexts are left to Ruby's garbage collector, resident memory peaks at 102 MB against 2,920 MB.
28
28
  - **Forks pause worker work, and children use their own threads.** For forks made through Ruby's `fork`, `IO.popen("-")`, or `Process.daemon`, mini_racer_rs waits for context runners, pauses its worker pool, and waits for running tasks; an async `WebAssembly.compile` in a new default-mode child completes on the child's own pool. On Ubuntu 24.04 with glibc 2.39, 20 of 20 mini_racer children aborted with signal 6 after loading and rendering React, while 20 of 20 mini_racer_rs children exited cleanly. On Fedora, mini_racer children hung within the first 50 forks of `test/stress/fork_busy.rb`, while mini_racer_rs completed 300 forks per run on Fedora and Ubuntu.
29
29
  - **V8 15.2.124.1.** mini_racer 0.22.1 uses V8 13.6.233.17.
30
30
  - **No compiler at release install.** Native gems cover glibc Linux on x86_64 and aarch64, plus macOS on arm64 and x86_64. Each carries extensions for Ruby 3.3, 3.4, and 4.0. mini_racer 0.22.1 ships as source and compiles C++ against libv8-node.
@@ -156,21 +156,27 @@ gem "mini_racer", github: "andymai/mini_racer_rs", tag: "vX.Y.Z", glob: "shim/*.
156
156
 
157
157
  The repository tests shim resolution with `path:` sources and rtlcss in `test/consumers/transitive/`. The `github:` form has not been run end to end. A dependency whose `mini_racer` requirement excludes version 0.22.1 cannot use the shim.
158
158
 
159
+ ### Forking servers such as Puma
160
+
161
+ Prime the process-wide V8 code cache before forking by evaluating each bundle of 1 MB or more twice in the master, using exactly the source text and file name the renderer uses, then dispose those contexts. The second evaluation builds the cache, later matching evaluations compile from it, and forked children inherit it. For Babel's 2.3 MB bundle at commit `5e13c1c`, a forked child's first load took, as medians across 5 trials, 60.8 ms when the parent had not loaded it, 65.5 ms after one load, and 25.0 ms after two loads. You can prime a `react-rails` app by building its server renderer twice. Parent contexts are not used in children. Only the cache carries over.
162
+
163
+ With `mini_racer` 0.22.1, each worker compiles the bundle from source on its first load because there is no code cache.
164
+
159
165
  ## Benchmarks
160
166
 
161
167
  | Workload | mini_racer 0.22.1 | mini_racer_rs | Ratio |
162
168
  |---|---:|---:|---:|
163
- | Babel transform of a 3 KB JSX component, through ExecJS | 10.0 ms | 5.3 ms | 1.9x |
164
- | Terser minify of a 78 KB library, through ExecJS | 51.6 ms | 51.2 ms | 1.0x |
165
- | React server render of 1,000 items (17 KB of HTML), through ExecJS | 192 µs | 174 µs | 1.1x |
166
- | React in a new context: load the bundle and render 1,000 items, through ExecJS | 12.0 ms | 12.1 ms | 1.0x |
167
- | Handlebars render of 50 rows, through `Context#call` | 47.9 µs | 40.1 µs | 1.2x |
168
- | Autoprefixer on Bootstrap's 280 KB stylesheet, through ExecJS | 27.2 ms | 23.7 ms | 1.1x |
169
- | MUI dashboard server render of 200 rows and 299 icons (469 KB of HTML), through ExecJS | 32.2 ms | 30.5 ms | 1.1x |
170
- | MUI in a new context: load the 8.2 MB bundle and render once, through ExecJS | 167 ms | 121 ms | 1.4x |
169
+ | Babel transform of a 3 KB JSX component, through ExecJS | 10.1 ms | 5.2 ms | 2.0x |
170
+ | Terser minify of a 78 KB library, through ExecJS | 51.1 ms | 52.0 ms | 1.0x |
171
+ | React server render of 1,000 items (17 KB of HTML), through ExecJS | 191 µs | 174 µs | 1.1x |
172
+ | React in a new context: load the bundle and render 1,000 items, through ExecJS | 11.9 ms | 11.8 ms | 1.0x |
173
+ | Handlebars render of 50 rows, through `Context#call` | 49.1 µs | 34.9 µs | 1.4x |
174
+ | Autoprefixer on Bootstrap's 280 KB stylesheet, through ExecJS | 26.3 ms | 23.7 ms | 1.1x |
175
+ | MUI dashboard server render of 200 rows and 299 icons (469 KB of HTML), through ExecJS | 32.6 ms | 29.8 ms | 1.1x |
176
+ | MUI in a new context: load the 8.2 MB bundle and render once, through ExecJS | 167 ms | 105 ms | 1.6x |
171
177
  | Load a 44 MB production server rendering bundle into a new context, through ExecJS | 983 ms | 215 ms | 4.6x |
172
178
 
173
- The table reports the eight library workloads plus the 44 MB production server rendering bundle load. Results are medians of 5 runs from the release build of commit `2513a48`, with every workload producing identical output in both gems. They were measured on Ruby 3.4.2 and Linux on an AMD Ryzen 9 7950X3D, with the CPU governor set to `performance` and both gems pinned to CPUs 0 through 7.
179
+ Results are medians of 5 runs, with every workload producing identical output in both gems. The eight library workloads use the release build of commit `220bfe7`, and the 44 MB production server rendering bundle load uses commit `2513a48`. Measurements ran on Ruby 3.4.2 and Linux on an AMD Ryzen 9 7950X3D, with the CPU governor set to `performance` and both gems pinned to CPUs 0 through 7.
174
180
 
175
181
  ```sh
176
182
  bundle exec ruby bench/run.rb --cpus 0-7
@@ -14,8 +14,10 @@ mod spin;
14
14
  reason = "the engine's only FFI fence: symbols the linker routes into V8"
15
15
  )]
16
16
  mod sys;
17
+ mod text;
17
18
 
18
19
  pub use context::{ContextHandle, Host, Unparker, WaitHandle, WaitOutcome};
19
20
  pub use control::{CallRequest, CallbackReply, ContextConfig, JsText};
20
21
  pub use error::{EngineError, FlagError, InvalidSnapshot, JsError, JsErrorKind, TerminationCause};
21
22
  pub use snapshot::{SnapshotBlob, WeakSnapshotBlob};
23
+ pub use text::{Decoded, Transcoder};
@@ -21,8 +21,9 @@ const SAMPLE_POINTS: usize = 8;
21
21
  /// compiling it again.
22
22
  ///
23
23
  /// A script is cached the second time the process sees it. The first sighting
24
- /// costs a sampled digest, not a full one or a cache, as a large script
25
- /// evaluated once (per-request data, say) gains nothing from either.
24
+ /// costs a sampled digest and no cache, as a large script evaluated once
25
+ /// (per-request data, say) gains nothing from one; it costs the full digest
26
+ /// too only in a context that reads the script from its shared source.
26
27
  #[derive(Default)]
27
28
  pub struct CodeCache {
28
29
  // Most recently used last.
@@ -77,8 +78,9 @@ impl Drop for Building {
77
78
 
78
79
  /// How the runner compiles a large script.
79
80
  pub(crate) enum Plan {
80
- /// From source: a first sighting or the cache is off, or with its key, a
81
- /// key being built elsewhere or a refused cache.
81
+ /// From source: the cache is off or a first sighting, or with its key, a
82
+ /// first sighting whose source is shared, a key being built elsewhere or
83
+ /// a refused cache.
82
84
  Compile(Option<Key>),
83
85
  /// From source, then a cache made once the dispatch is over.
84
86
  Make(Building),
@@ -87,7 +89,7 @@ pub(crate) enum Plan {
87
89
  }
88
90
 
89
91
  impl Plan {
90
- /// The key of a script the process has seen before.
92
+ /// The script's key, unless the plan needed none.
91
93
  pub(crate) const fn key(&self) -> Option<&Key> {
92
94
  match self {
93
95
  Self::Compile(key) => key.as_ref(),
@@ -98,11 +100,14 @@ impl Plan {
98
100
  }
99
101
 
100
102
  impl CodeCache {
103
+ /// `shared` asks for a first sighting's key as well, which only a
104
+ /// context reading the script from its shared source needs.
101
105
  pub(crate) fn plan(
102
106
  &'static self,
103
107
  filename: &JsText,
104
108
  source: &JsText,
105
109
  snapshot: Option<u64>,
110
+ shared: bool,
106
111
  ) -> Plan {
107
112
  if !enabled() {
108
113
  return Plan::Compile(None);
@@ -113,7 +118,8 @@ impl CodeCache {
113
118
  let cached = lock(&self.entries)
114
119
  .iter()
115
120
  .any(|entry| entry.key.sighting == sighting);
116
- if !seen && !cached {
121
+ let first = !seen && !cached;
122
+ if first && !shared {
117
123
  return Plan::Compile(None);
118
124
  }
119
125
  let key = Key {
@@ -123,6 +129,9 @@ impl CodeCache {
123
129
  digest: self.hasher.hash_one(source),
124
130
  sighting,
125
131
  };
132
+ if first {
133
+ return Plan::Compile(Some(key));
134
+ }
126
135
  let mut entries = lock(&self.entries);
127
136
  if let Some(at) = entries.iter().position(|entry| entry.key == key) {
128
137
  let entry = entries.remove(at);
@@ -300,8 +309,8 @@ mod tests {
300
309
 
301
310
  // Sees `text` twice and stores a cache for it.
302
311
  fn cached(cache: &'static CodeCache, text: &JsText, data: Box<[u8]>) {
303
- let _ = cache.plan(&name(), text, None);
304
- let Plan::Make(building) = cache.plan(&name(), text, None) else {
312
+ let _ = cache.plan(&name(), text, None, true);
313
+ let Plan::Make(building) = cache.plan(&name(), text, None, true) else {
305
314
  panic!("a second sighting makes a cache");
306
315
  };
307
316
  cache.store(building, data);
@@ -311,22 +320,46 @@ mod tests {
311
320
  fn caches_a_script_the_second_time_it_is_seen() {
312
321
  let cache = cache();
313
322
  assert!(matches!(
314
- cache.plan(&name(), &source(1), None),
315
- Plan::Compile(None)
323
+ cache.plan(&name(), &source(1), None, true),
324
+ Plan::Compile(Some(_))
316
325
  ));
317
326
  cached(cache, &source(1), Box::from(&b"compiled"[..]));
318
327
  assert!(matches!(
319
- cache.plan(&name(), &source(1), None),
328
+ cache.plan(&name(), &source(1), None, true),
320
329
  Plan::Consume(..)
321
330
  ));
322
331
  assert!(matches!(
323
- cache.plan(&name(), &source(1), Some(7)),
324
- Plan::Compile(None)
332
+ cache.plan(&name(), &source(1), Some(7), true),
333
+ Plan::Compile(Some(_))
325
334
  ));
326
335
  assert!(matches!(
327
- cache.plan(&JsText::from("other.js"), &source(1), None),
336
+ cache.plan(&JsText::from("other.js"), &source(1), None, true),
337
+ Plan::Compile(Some(_))
338
+ ));
339
+ }
340
+
341
+ // The key a first sighting has for its shared source is the one the
342
+ // cache is then made and found under.
343
+ #[test]
344
+ fn a_first_sighting_has_its_key_only_for_a_shared_source() {
345
+ let cache = cache();
346
+ assert!(matches!(
347
+ cache.plan(&name(), &source(8), None, false),
328
348
  Plan::Compile(None)
329
349
  ));
350
+ let first = cache.plan(&name(), &source(9), None, true);
351
+ assert!(matches!(first, Plan::Compile(Some(_))));
352
+ let second = cache.plan(&name(), &source(9), None, false);
353
+ assert!(matches!(second, Plan::Make(..)));
354
+ assert!(first.key() == second.key());
355
+ let Plan::Make(building) = second else {
356
+ panic!("a second sighting makes a cache");
357
+ };
358
+ cache.store(building, Box::from(&b"compiled"[..]));
359
+ let Plan::Consume(_, key) = cache.plan(&name(), &source(9), None, true) else {
360
+ panic!("a stored cache is found");
361
+ };
362
+ assert!(first.key() == Some(&key));
330
363
  }
331
364
 
332
365
  // Sources that match on every sampled unit and differ elsewhere share a
@@ -344,7 +377,10 @@ mod tests {
344
377
  cache.sighting(&name(), &second, None)
345
378
  );
346
379
  cached(cache, &first, Box::from(&b"compiled"[..]));
347
- assert!(matches!(cache.plan(&name(), &second, None), Plan::Make(..)));
380
+ assert!(matches!(
381
+ cache.plan(&name(), &second, None, true),
382
+ Plan::Make(..)
383
+ ));
348
384
  }
349
385
 
350
386
  #[test]
@@ -353,10 +389,10 @@ mod tests {
353
389
  cached(cache, &source(2), Box::from(&b"compiled"[..]));
354
390
  // More sightings than the list keeps.
355
391
  for seed in 10_u8..80 {
356
- let _ = cache.plan(&name(), &source(seed), None);
392
+ let _ = cache.plan(&name(), &source(seed), None, false);
357
393
  }
358
394
  assert!(matches!(
359
- cache.plan(&name(), &source(2), None),
395
+ cache.plan(&name(), &source(2), None, true),
360
396
  Plan::Consume(..)
361
397
  ));
362
398
  }
@@ -364,15 +400,15 @@ mod tests {
364
400
  #[test]
365
401
  fn a_script_being_built_is_built_once() {
366
402
  let cache = cache();
367
- let _ = cache.plan(&name(), &source(3), None);
368
- let first = cache.plan(&name(), &source(3), None);
403
+ let _ = cache.plan(&name(), &source(3), None, true);
404
+ let first = cache.plan(&name(), &source(3), None, true);
369
405
  assert!(matches!(first, Plan::Make(..)));
370
- let elsewhere = cache.plan(&name(), &source(3), None);
406
+ let elsewhere = cache.plan(&name(), &source(3), None, true);
371
407
  assert!(matches!(elsewhere, Plan::Compile(Some(_))));
372
408
  assert!(elsewhere.key() == first.key());
373
409
  drop(first);
374
410
  assert!(matches!(
375
- cache.plan(&name(), &source(3), None),
411
+ cache.plan(&name(), &source(3), None, true),
376
412
  Plan::Make(..)
377
413
  ));
378
414
  }
@@ -383,7 +419,7 @@ mod tests {
383
419
  cached(cache, &source(4), Box::from(&b"compiled"[..]));
384
420
  let child: &'static CodeCache = Box::leak(Box::new(cache.inherited()));
385
421
  assert!(matches!(
386
- child.plan(&name(), &source(4), None),
422
+ child.plan(&name(), &source(4), None, true),
387
423
  Plan::Consume(..)
388
424
  ));
389
425
  }
@@ -396,7 +432,7 @@ mod tests {
396
432
  let child: &'static CodeCache = Box::leak(Box::new(cache.inherited()));
397
433
  drop(held);
398
434
  assert!(matches!(
399
- child.plan(&name(), &source(5), None),
435
+ child.plan(&name(), &source(5), None, true),
400
436
  Plan::Make(..)
401
437
  ));
402
438
  }
@@ -412,11 +448,11 @@ mod tests {
412
448
  );
413
449
  assert_eq!(cache.stored(), 1);
414
450
  assert!(matches!(
415
- cache.plan(&name(), &source(7), None),
451
+ cache.plan(&name(), &source(7), None, true),
416
452
  Plan::Compile(Some(_))
417
453
  ));
418
454
  assert!(matches!(
419
- cache.plan(&name(), &source(6), None),
455
+ cache.plan(&name(), &source(6), None, true),
420
456
  Plan::Consume(..)
421
457
  ));
422
458
  }