module: implement flushCompileCache() · nodejs/node@06957ff · GitHub
Skip to content

Commit 06957ff

Browse files
joyeecheungtargos
authored andcommitted
module: implement flushCompileCache()
This implements an API for users to intentionally flush the accumulated compile cache instead of waiting until process shutdown. It may be useful for application that loads dependencies first and then either reload itself in other instances, or spawning other instances that load an overlapping set of its dependencies - in this case its useful to flush the cache early instead of waiting until the shutdown of itself. Currently flushing is triggered by either process shutdown or user requests. In the future we should simply start the writes right after module loading on a separate thread, and this method only blocks until all the pending writes (if any) on the other thread are finished. In that case, the off-thread writes should finish long before any attempt of flushing is made so the method would then only incur a negligible overhead from thread synchronization. PR-URL: #54971 Fixes: #54770 Fixes: #54465 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 2dcf70c commit 06957ff

8 files changed

Lines changed: 130 additions & 8 deletions

File tree

doc/api/module.md

Lines changed: 23 additions & 0 deletions

lib/internal/modules/helpers.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const {
4040
enableCompileCache: _enableCompileCache,
4141
getCompileCacheDir: _getCompileCacheDir,
4242
compileCacheStatus: _compileCacheStatus,
43+
flushCompileCache,
4344
} = internalBinding('modules');
4445

4546
let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
@@ -484,6 +485,7 @@ module.exports = {
484485
constants,
485486
enableCompileCache,
486487
assertBufferSource,
488+
flushCompileCache,
487489
getBuiltinModule,
488490
getCjsConditions,
489491
getCompileCacheDir,

lib/module.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { SourceMap } = require('internal/source_map/source_map');
77
const {
88
constants,
99
enableCompileCache,
10+
flushCompileCache,
1011
getCompileCacheDir,
1112
} = require('internal/modules/helpers');
1213

@@ -15,5 +16,7 @@ Module.register = register;
1516
Module.SourceMap = SourceMap;
1617
Module.constants = constants;
1718
Module.enableCompileCache = enableCompileCache;
19+
Module.flushCompileCache = flushCompileCache;
20+
1821
Module.getCompileCacheDir = getCompileCacheDir;
1922
module.exports = Module;

src/compile_cache.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,13 @@ void CompileCacheHandler::Persist() {
309309

310310
// TODO(joyeecheung): do this using a separate event loop to utilize the
311311
// 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.
312319
for (auto& pair : compiler_cache_store_) {
313320
auto* entry = pair.second.get();
314321
if (entry->cache == nullptr) {

src/env.cc

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -858,14 +858,12 @@ Environment::Environment(IsolateData* isolate_data,
858858
}
859859
}
860860

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+
}
869867

870868
// We'll be creating new objects so make sure we've entered the context.
871869
HandleScope handle_scope(isolate);

src/node_modules.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,25 @@ void BindingData::GetPackageScopeConfig(
435435
.ToLocalChecked());
436436
}
437437

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+
438457
void EnableCompileCache(const FunctionCallbackInfo<Value>& args) {
439458
Isolate* isolate = args.GetIsolate();
440459
Local<Context> context = isolate->GetCurrentContext();
@@ -480,6 +499,7 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
480499
SetMethod(isolate, target, "getPackageScopeConfig", GetPackageScopeConfig);
481500
SetMethod(isolate, target, "enableCompileCache", EnableCompileCache);
482501
SetMethod(isolate, target, "getCompileCacheDir", GetCompileCacheDir);
502+
SetMethod(isolate, target, "flushCompileCache", FlushCompileCache);
483503
}
484504

485505
void BindingData::CreatePerContextProperties(Local<Object> target,
@@ -512,6 +532,7 @@ void BindingData::RegisterExternalReferences(
512532
registry->Register(GetPackageScopeConfig);
513533
registry->Register(EnableCompileCache);
514534
registry->Register(GetCompileCacheDir);
535+
registry->Register(FlushCompileCache);
515536
}
516537

517538
} // namespace modules
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
}
Lines changed: 47 additions & 0 deletions

0 commit comments

Comments
 (0)