Skip to content

User-land snapshot JS API: request for feedback #42617

Closed
@joyeecheung

Description

@joyeecheung

To improve the user experience of the user land snapshot, (quoting myself from #38905 (comment)) we need an API to specify user-land deserializers and deserialized main functions so that when starting up the application from the snapshot, the main script isn't necessary if the user already bakes a main function into the snapshot, with build-time snapshots (and maybe also run-time snapshots in later iterations) this could effectively turn the result into a single-file executable of an application.

I have a non-functional WIP here, my current idea is that in Node.js instances started in snapshot-building mode, we provide two hooks to the user, exposed by the v8.snapshot namespace, to specify user-land deserializers and the main script (ideas of better naming or better hooks are welcomed):

  • addDeserializer(func, data): can be invoked multiple times in difference places where the user needs to serialize/synchronize something that's not pure JS (e.g. system resources), the deserializers added will be invoked when the snapshot is deserialized. The second data parameter passed into it will be passed into the func when it's invoked (similar to how we handle the timer callbacks)
    • Note: modules can also detect the existence of addDeserializer from v8.snapshot to see if the application is run in snapshot building mode, so that they can add deserializers as needed.
  • setDeserializedMainFunction(func, data): as the name implies this can only be invoked once, it throws an error on the second attempt. If the main function is set, the deserialized application does not need another entry point script specified from the command line when being started, instead the function specified will be invoked after all the deserializers are invoked.

Example: assuming the following snippet is saved as snapshot.js, the binary can be built with configure --node-snapshot-main=snapshot.js at build time (requires compiling Node.js from source):

// In the snapshot main script passed to the --node-snapshot-main configure option:
const fs = require('fs');
const {
  addDeserializer,
  setDeserializedMainFunction
} = require('v8').snapshot;

// An example of allocating system resources (the fs stream) that need to be handled
// across snapshot serialization/deserialization boundaries:
function startHistory(filename) {
  const stream = fs.createWriteStream(filename, { flags: 'a' });
  const interval = setInterval(() => {
    stream.write('some history\n');
  }, 500);
  interval.unref();
  return stream;
}

let stream = startHistory('/tmp/history.txt');
addDeserializer(({filename}) => {
  stream = startHistory(filename);
}, { filename });  // The second parameter gets passed into the function.

// Do other stuff..

// Release the system resources that can be recreated later when the snapshot
// is deserialized.
stream.end();

setDeserializedMainFunction(
  (data) => {
    // If the binary with the snapshot is started as `node arg1 arg2`,
    // process.argv[1] would be 'arg1' and process.argv[2] would be 'arg2',
    // etc.
  },
  data  // The second parameter gets passed into the function.
);

// If the main function is not set, the binary with the snapshot needs to be started with an
// additional entry point like `node index.js arg1 arg2`,
// and process.argv[1] would be 'index.js',  process.argv[2]  would be 'arg1',
// etc.

With the run-time snapshot (at least the initial iteration), the snapshot blob would be written to disk (e.g. node --build-snapshot snapshot.js creates a snapshot.blob at the current working directory), so the user still needs to start the application with another snapshot blob file (e.g. node --snapshot-blob snapshot.blob arg1 arg2). In the next iteration though, we could create a copy of the binary and then append that blob to create a single-file executable, so that users can do this without having to compile Node.js from source.

Refs: #35711

Metadata

Metadata

Assignees

No one assigned

    Labels

    snapshotIssues and PRs related to the startup snapshot

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions