| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 2dcf70c commit 06957ff
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -199,6 +199,13 @@ Compilation cache generated by one version of Node.js can not be reused by a dif | |||
| 199 | 199 | version of Node.js. Cache generated by different versions of Node.js will be stored | |
| 200 | 200 | separately if the same base directory is used to persist the cache, so they can co-exist. | |
| 201 | 201 | ||
| 202 | + At the moment, when the compile cache is enabled and a module is loaded afresh, the | ||
| 203 | + code cache is generated from the compiled code immediately, but will only be written | ||
| 204 | + to disk when the Node.js instance is about to exit. This is subject to change. The | ||
| 205 | + [`module.flushCompileCache()`][] method can be used to ensure the accumulated code cache | ||
| 206 | + is flushed to disk in case the application wants to spawn other Node.js instances | ||
| 207 | + and let them share the cache long before the parent exits. | ||
| 208 | + | ||
| 202 | 209 | ### `module.getCompileCacheDir()` | |
| 203 | 210 | ||
| 204 | 211 | <!-- YAML | |
@@ -1101,6 +1108,21 @@ added: | |||
| 1101 | 1108 | `path` is the resolved path for the file for which a corresponding source map | |
| 1102 | 1109 | should be fetched. | |
| 1103 | 1110 | ||
| 1111 | + ### `module.flushCompileCache()` | ||
| 1112 | + | ||
| 1113 | + <!-- YAML | ||
| 1114 | + added: | ||
| 1115 | + - REPLACEME | ||
| 1116 | + --> | ||
| 1117 | + | ||
| 1118 | + > Stability: 1.1 - Active Development | ||
| 1119 | + | ||
| 1120 | + Flush the [module compile cache][] accumulated from modules already loaded | ||
| 1121 | + in the current Node.js instance to disk. This returns after all the flushing | ||
| 1122 | + file system operations come to an end, no matter they succeed or not. If there | ||
| 1123 | + are any errors, this will fail silently, since compile cache misses should not | ||
| 1124 | + interfer with the actual operation of the application. | ||
| 1125 | + | ||
| 1104 | 1126 | ### Class: `module.SourceMap` | |
| 1105 | 1127 | ||
| 1106 | 1128 | <!-- YAML | |
@@ -1216,6 +1238,7 @@ returned object contains the following keys: | |||
| 1216 | 1238 | [`initialize`]: #initialize | |
| 1217 | 1239 | [`module.constants.compileCacheStatus`]: #moduleconstantscompilecachestatus | |
| 1218 | 1240 | [`module.enableCompileCache()`]: #moduleenablecompilecachecachedir | |
| 1241 | + [`module.flushCompileCache()`]: #moduleflushcompilecache | ||
| 1219 | 1242 | [`module.getCompileCacheDir()`]: #modulegetcompilecachedir | |
| 1220 | 1243 | [`module`]: #the-module-object | |
| 1221 | 1244 | [`os.tmpdir()`]: os.md#ostmpdir | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -40,6 +40,7 @@ const { | |||
| 40 | 40 | enableCompileCache: _enableCompileCache, | |
| 41 | 41 | getCompileCacheDir: _getCompileCacheDir, | |
| 42 | 42 | compileCacheStatus: _compileCacheStatus, | |
| 43 | + flushCompileCache, | ||
| 43 | 44 | } = internalBinding('modules'); | |
| 44 | 45 | ||
| 45 | 46 | let debug = require('internal/util/debuglog').debuglog('module', (fn) => { | |
@@ -484,6 +485,7 @@ module.exports = { | |||
| 484 | 485 | constants, | |
| 485 | 486 | enableCompileCache, | |
| 486 | 487 | assertBufferSource, | |
| 488 | + flushCompileCache, | ||
| 487 | 489 | getBuiltinModule, | |
| 488 | 490 | getCjsConditions, | |
| 489 | 491 | getCompileCacheDir, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,6 +7,7 @@ const { SourceMap } = require('internal/source_map/source_map'); | |||
| 7 | 7 | const { | |
| 8 | 8 | constants, | |
| 9 | 9 | enableCompileCache, | |
| 10 | + flushCompileCache, | ||
| 10 | 11 | getCompileCacheDir, | |
| 11 | 12 | } = require('internal/modules/helpers'); | |
| 12 | 13 | ||
@@ -15,5 +16,7 @@ Module.register = register; | |||
| 15 | 16 | Module.SourceMap = SourceMap; | |
| 16 | 17 | Module.constants = constants; | |
| 17 | 18 | Module.enableCompileCache = enableCompileCache; | |
| 19 | + Module.flushCompileCache = flushCompileCache; | ||
| 20 | + | ||
| 18 | 21 | Module.getCompileCacheDir = getCompileCacheDir; | |
| 19 | 22 | module.exports = Module; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -309,6 +309,13 @@ void CompileCacheHandler::Persist() { | |||
| 309 | 309 | ||
| 310 | 310 | // TODO(joyeecheung): do this using a separate event loop to utilize the | |
| 311 | 311 | // libuv thread pool and do the file system operations concurrently. | |
| 312 | + // TODO(joyeecheung): Currently flushing is triggered by either process | ||
| 313 | + // shutdown or user requests. In the future we should simply start the | ||
| 314 | + // writes right after module loading on a separate thread, and this method | ||
| 315 | + // only blocks until all the pending writes (if any) on the other thread are | ||
| 316 | + // finished. In that case, the off-thread writes should finish long | ||
| 317 | + // before any attempt of flushing is made so the method would then only | ||
| 318 | + // incur a negligible overhead from thread synchronization. | ||
| 312 | 319 | for (auto& pair : compiler_cache_store_) { | |
| 313 | 320 | auto* entry = pair.second.get(); | |
| 314 | 321 | if (entry->cache == nullptr) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -858,14 +858,12 @@ Environment::Environment(IsolateData* isolate_data, | |||
| 858 | 858 | } | |
| 859 | 859 | } | |
| 860 | 860 | ||
| 861 | - // We are supposed to call builtin_loader_.SetEagerCompile() in | ||
| 862 | - // snapshot mode here because it's beneficial to compile built-ins | ||
| 863 | - // loaded in the snapshot eagerly and include the code of inner functions | ||
| 864 | - // that are likely to be used by user since they are part of the core | ||
| 865 | - // startup. But this requires us to start the coverage collections | ||
| 866 | - // before Environment/Context creation which is not currently possible. | ||
| 867 | - // TODO(joyeecheung): refactor V8ProfilerConnection classes to parse | ||
| 868 | - // JSON without v8 and lift this restriction. | ||
| 861 | + // Compile builtins eagerly when building the snapshot so that inner functions | ||
| 862 | + // of essential builtins that are loaded in the snapshot can have faster first | ||
| 863 | + // invocation. | ||
| 864 | + if (isolate_data->is_building_snapshot()) { | ||
| 865 | + builtin_loader()->SetEagerCompile(); | ||
| 866 | + } | ||
| 869 | 867 | ||
| 870 | 868 | // We'll be creating new objects so make sure we've entered the context. | |
| 871 | 869 | HandleScope handle_scope(isolate); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -435,6 +435,25 @@ void BindingData::GetPackageScopeConfig( | |||
| 435 | 435 | .ToLocalChecked()); | |
| 436 | 436 | } | |
| 437 | 437 | ||
| 438 | + void FlushCompileCache(const FunctionCallbackInfo<Value>& args) { | ||
| 439 | + Isolate* isolate = args.GetIsolate(); | ||
| 440 | + Local<Context> context = isolate->GetCurrentContext(); | ||
| 441 | + Environment* env = Environment::GetCurrent(context); | ||
| 442 | + | ||
| 443 | + if (!args[0]->IsBoolean() && !args[0]->IsUndefined()) { | ||
| 444 | + THROW_ERR_INVALID_ARG_TYPE(env, | ||
| 445 | + "keepDeserializedCache should be a boolean"); | ||
| 446 | + return; | ||
| 447 | + } | ||
| 448 | + Debug(env, | ||
| 449 | + DebugCategory::COMPILE_CACHE, | ||
| 450 | + "[compile cache] module.flushCompileCache() requested.\n"); | ||
| 451 | + env->FlushCompileCache(); | ||
| 452 | + Debug(env, | ||
| 453 | + DebugCategory::COMPILE_CACHE, | ||
| 454 | + "[compile cache] module.flushCompileCache() finished.\n"); | ||
| 455 | + } | ||
| 456 | + | ||
| 438 | 457 | void EnableCompileCache(const FunctionCallbackInfo<Value>& args) { | |
| 439 | 458 | Isolate* isolate = args.GetIsolate(); | |
| 440 | 459 | Local<Context> context = isolate->GetCurrentContext(); | |
@@ -480,6 +499,7 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, | |||
| 480 | 499 | SetMethod(isolate, target, "getPackageScopeConfig", GetPackageScopeConfig); | |
| 481 | 500 | SetMethod(isolate, target, "enableCompileCache", EnableCompileCache); | |
| 482 | 501 | SetMethod(isolate, target, "getCompileCacheDir", GetCompileCacheDir); | |
| 502 | + SetMethod(isolate, target, "flushCompileCache", FlushCompileCache); | ||
| 483 | 503 | } | |
| 484 | 504 | ||
| 485 | 505 | void BindingData::CreatePerContextProperties(Local<Object> target, | |
@@ -512,6 +532,7 @@ void BindingData::RegisterExternalReferences( | |||
| 512 | 532 | registry->Register(GetPackageScopeConfig); | |
| 513 | 533 | registry->Register(EnableCompileCache); | |
| 514 | 534 | registry->Register(GetCompileCacheDir); | |
| 535 | + registry->Register(FlushCompileCache); | ||
| 515 | 536 | } | |
| 516 | 537 | ||
| 517 | 538 | } // namespace modules | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,21 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const { flushCompileCache, getCompileCacheDir } = require('module'); | ||
| 4 | + const { spawnSync } = require('child_process'); | ||
| 5 | + const assert = require('assert'); | ||
| 6 | + | ||
| 7 | + if (process.argv[2] !== 'child') { | ||
| 8 | + // The test should be run with the compile cache already enabled and NODE_DEBUG_NATIVE=COMPILE_CACHE. | ||
| 9 | + assert(getCompileCacheDir()); | ||
| 10 | + assert(process.env.NODE_DEBUG_NATIVE.includes('COMPILE_CACHE')); | ||
| 11 | + | ||
| 12 | + flushCompileCache(); | ||
| 13 | + | ||
| 14 | + const child1 = spawnSync(process.execPath, [__filename, 'child']); | ||
| 15 | + console.log(child1.stderr.toString().trim().split('\n').map(line => `[child1]${line}`).join('\n')); | ||
| 16 | + | ||
| 17 | + flushCompileCache(); | ||
| 18 | + | ||
| 19 | + const child2 = spawnSync(process.execPath, [__filename, 'child']); | ||
| 20 | + console.log(child2.stderr.toString().trim().split('\n').map(line => `[child2]${line}`).join('\n')); | ||
| 21 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,47 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + // This tests module.flushCompileCache() works as expected. | ||
| 4 | + | ||
| 5 | + require('../common'); | ||
| 6 | + const { spawnSyncAndAssert } = require('../common/child_process'); | ||
| 7 | + const assert = require('assert'); | ||
| 8 | + const tmpdir = require('../common/tmpdir'); | ||
| 9 | + const fixtures = require('../common/fixtures'); | ||
| 10 | + | ||
| 11 | + { | ||
| 12 | + // Test that it works with non-existent directory. | ||
| 13 | + tmpdir.refresh(); | ||
| 14 | + const cacheDir = tmpdir.resolve('compile_cache'); | ||
| 15 | + spawnSyncAndAssert( | ||
| 16 | + process.execPath, | ||
| 17 | + [fixtures.path('compile-cache-flush.js')], | ||
| 18 | + { | ||
| 19 | + env: { | ||
| 20 | + ...process.env, | ||
| 21 | + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', | ||
| 22 | + NODE_COMPILE_CACHE: cacheDir, | ||
| 23 | + }, | ||
| 24 | + cwd: tmpdir.path | ||
| 25 | + }, | ||
| 26 | + { | ||
| 27 | + stdout(output) { | ||
| 28 | + // This contains output from the nested spawnings of compile-cache-flush.js. | ||
| 29 | + assert.match(output, /child1.* cache for .*compile-cache-flush\.js was accepted, keeping the in-memory entry/); | ||
| 30 | + assert.match(output, /child2.* cache for .*compile-cache-flush\.js was accepted, keeping the in-memory entry/); | ||
| 31 | + return true; | ||
| 32 | + }, | ||
| 33 | + stderr(output) { | ||
| 34 | + // This contains output from the top-level spawning of compile-cache-flush.js. | ||
| 35 | + assert.match(output, /reading cache from .*compile_cache.* for CommonJS .*compile-cache-flush\.js/); | ||
| 36 | + assert.match(output, /compile-cache-flush\.js was not initialized, initializing the in-memory entry/); | ||
| 37 | + | ||
| 38 | + const writeRE = /writing cache for .*compile-cache-flush\.js.*success/; | ||
| 39 | + const flushRE = /module\.flushCompileCache\(\) finished/; | ||
| 40 | + assert.match(output, writeRE); | ||
| 41 | + assert.match(output, flushRE); | ||
| 42 | + // The cache writing should happen before flushing finishes i.e. it's not delayed until process shutdown. | ||
| 43 | + assert(output.match(writeRE).index < output.match(flushRE).index); | ||
| 44 | + return true; | ||
| 45 | + } | ||
| 46 | + }); | ||
| 47 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments