Skip to content

Commit e4539e1

Browse files
joyeecheungMoLow
authored andcommitted
doc,test: update the v8.startupSnapshot doc and test the example
The API is now available to user-land run-time snapshots. So update the example. This also makes the intention of the examples a bit clearer and test it in our test suite. PR-URL: #47468 Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent be49669 commit e4539e1

File tree

3 files changed

+124
-67
lines changed

3 files changed

+124
-67
lines changed

doc/api/v8.md

+53-32
Original file line numberDiff line numberDiff line change
@@ -899,15 +899,12 @@ added: v18.6.0
899899
> Stability: 1 - Experimental
900900
901901
The `v8.startupSnapshot` interface can be used to add serialization and
902-
deserialization hooks for custom startup snapshots. Currently the startup
903-
snapshots can only be built into the Node.js binary from source.
902+
deserialization hooks for custom startup snapshots.
904903

905904
```console
906-
$ cd /path/to/node
907-
$ ./configure --node-snapshot-main=entry.js
908-
$ make node
909-
# This binary contains the result of the execution of entry.js
910-
$ out/Release/node
905+
$ node --snapshot-blob snapshot.blob --build-snapshot entry.js
906+
# This launches a process with the snapshot
907+
$ node --snapshot-blob snapshot.blob
911908
```
912909

913910
In the example above, `entry.js` can use methods from the `v8.startupSnapshot`
@@ -924,42 +921,66 @@ const zlib = require('node:zlib');
924921
const path = require('node:path');
925922
const assert = require('node:assert');
926923

927-
const {
928-
isBuildingSnapshot,
929-
addSerializeCallback,
930-
addDeserializeCallback,
931-
setDeserializeMainFunction,
932-
} = require('node:v8').startupSnapshot;
924+
const v8 = require('node:v8');
933925

934-
const filePath = path.resolve(__dirname, '../x1024.txt');
935-
const storage = {};
926+
class BookShelf {
927+
storage = new Map();
936928

937-
assert(isBuildingSnapshot());
929+
// Reading a series of files from directory and store them into storage.
930+
constructor(directory, books) {
931+
for (const book of books) {
932+
this.storage.set(book, fs.readFileSync(path.join(directory, book)));
933+
}
934+
}
938935

939-
addSerializeCallback(({ filePath }) => {
940-
storage[filePath] = zlib.gzipSync(fs.readFileSync(filePath));
941-
}, { filePath });
936+
static compressAll(shelf) {
937+
for (const [ book, content ] of shelf.storage) {
938+
shelf.storage.set(book, zlib.gzipSync(content));
939+
}
940+
}
942941

943-
addDeserializeCallback(({ filePath }) => {
944-
storage[filePath] = zlib.gunzipSync(storage[filePath]);
945-
}, { filePath });
942+
static decompressAll(shelf) {
943+
for (const [ book, content ] of shelf.storage) {
944+
shelf.storage.set(book, zlib.gunzipSync(content));
945+
}
946+
}
947+
}
946948

947-
setDeserializeMainFunction(({ filePath }) => {
948-
console.log(storage[filePath].toString());
949-
}, { filePath });
949+
// __dirname here is where the snapshot script is placed
950+
// during snapshot building time.
951+
const shelf = new BookShelf(__dirname, [
952+
'book1.en_US.txt',
953+
'book1.es_ES.txt',
954+
'book2.zh_CN.txt',
955+
]);
956+
957+
assert(v8.startupSnapshot.isBuildingSnapshot());
958+
// On snapshot serialization, compress the books to reduce size.
959+
v8.startupSnapshot.addSerializeCallback(BookShelf.compressAll, shelf);
960+
// On snapshot deserialization, decompress the books.
961+
v8.startupSnapshot.addDeserializeCallback(BookShelf.decompressAll, shelf);
962+
v8.startupSnapshot.setDeserializeMainFunction((shelf) => {
963+
// process.env and process.argv are refreshed during snapshot
964+
// deserialization.
965+
const lang = process.env.BOOK_LANG || 'en_US';
966+
const book = process.argv[1];
967+
const name = `${book}.${lang}.txt`;
968+
console.log(shelf.storage.get(name));
969+
}, shelf);
950970
```
951971

952-
The resulted binary will simply print the data deserialized from the snapshot
953-
during start up:
972+
The resulted binary will get print the data deserialized from the snapshot
973+
during start up, using the refreshed `process.env` and `process.argv` of
974+
the launched process:
954975

955976
```console
956-
$ out/Release/node
957-
# Prints content of ./test/fixtures/x1024.txt
977+
$ BOOK_LANG=es_ES node --snapshot-blob snapshot.blob book1
978+
# Prints content of book1.es_ES.txt deserialized from the snapshot.
958979
```
959980

960-
Currently the API is only available to a Node.js instance launched from the
961-
default snapshot, that is, the application deserialized from a user-land
962-
snapshot cannot use these APIs again.
981+
Currently the application deserialized from a user-land snapshot cannot
982+
be snapshotted again, so these APIs are only available to applications
983+
that are not deserialized from a user-land snapshot.
963984

964985
### `v8.startupSnapshot.addSerializeCallback(callback[, data])`
965986

test/fixtures/snapshot/v8-startup-snapshot-api.js

+55-31
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,60 @@
11
'use strict';
22

3-
const fs = require('fs');
4-
const zlib = require('zlib');
5-
const path = require('path');
6-
const assert = require('assert');
7-
8-
const {
9-
isBuildingSnapshot,
10-
addSerializeCallback,
11-
addDeserializeCallback,
12-
setDeserializeMainFunction
13-
} = require('v8').startupSnapshot;
14-
15-
const filePath = path.resolve(__dirname, '../x1024.txt');
16-
const storage = {};
17-
18-
assert(isBuildingSnapshot());
19-
20-
addSerializeCallback(({ filePath }) => {
21-
console.error('serializing', filePath);
22-
storage[filePath] = zlib.gzipSync(fs.readFileSync(filePath));
23-
}, { filePath });
24-
25-
addDeserializeCallback(({ filePath }) => {
26-
console.error('deserializing', filePath);
27-
storage[filePath] = zlib.gunzipSync(storage[filePath]);
28-
}, { filePath });
29-
30-
setDeserializeMainFunction(({ filePath }) => {
31-
console.log(storage[filePath].toString());
32-
}, { filePath });
33-
assert.throws(() => setDeserializeMainFunction(() => {
3+
const fs = require('node:fs');
4+
const zlib = require('node:zlib');
5+
const path = require('node:path');
6+
const assert = require('node:assert');
7+
8+
const v8 = require('node:v8');
9+
10+
class BookShelf {
11+
storage = new Map();
12+
13+
// Reading a series of files from directory and store them into storage.
14+
constructor(directory, books) {
15+
for (const book of books) {
16+
this.storage.set(book, fs.readFileSync(path.join(directory, book)));
17+
};
18+
}
19+
20+
static compressAll(shelf) {
21+
for (const [ book, content ] of shelf.storage) {
22+
shelf.storage.set(book, zlib.gzipSync(content));
23+
}
24+
}
25+
26+
static decompressAll(shelf) {
27+
for (const [ book, content ] of shelf.storage) {
28+
shelf.storage.set(book, zlib.gunzipSync(content));
29+
}
30+
}
31+
}
32+
33+
// __dirname here is where the snapshot script is placed
34+
// during snapshot building time.
35+
const shelf = new BookShelf(__dirname, [
36+
'book1.en_US.txt',
37+
'book1.es_ES.txt',
38+
'book2.zh_CN.txt',
39+
]);
40+
41+
assert(v8.startupSnapshot.isBuildingSnapshot());
42+
43+
// On snapshot serialization, compress the books to reduce size.
44+
v8.startupSnapshot.addSerializeCallback(BookShelf.compressAll, shelf);
45+
// On snapshot deserialization, decompress the books.
46+
v8.startupSnapshot.addDeserializeCallback(BookShelf.decompressAll, shelf);
47+
v8.startupSnapshot.setDeserializeMainFunction((shelf) => {
48+
// process.env and process.argv are refreshed during snapshot
49+
// deserialization.
50+
const lang = process.env.BOOK_LANG || 'en_US';
51+
const book = process.argv[1];
52+
const name = `${book}.${lang}.txt`;
53+
console.error('Reading', name);
54+
console.log(shelf.storage.get(name).toString());
55+
}, shelf);
56+
57+
assert.throws(() => v8.startupSnapshot.setDeserializeMainFunction(() => {
3458
assert.fail('unreachable duplicated main function');
3559
}), {
3660
code: 'ERR_DUPLICATE_STARTUP_SNAPSHOT_MAIN_FUNCTION',

test/parallel/test-snapshot-api.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
// This tests snapshot JS API
3+
// This tests snapshot JS API using the example in the docs.
44

55
require('../common');
66
const assert = require('assert');
@@ -20,11 +20,20 @@ tmpdir.refresh();
2020
const blobPath = path.join(tmpdir.path, 'snapshot.blob');
2121
const entry = fixtures.path('snapshot', 'v8-startup-snapshot-api.js');
2222
{
23+
for (const book of [
24+
'book1.en_US.txt',
25+
'book1.es_ES.txt',
26+
'book2.zh_CN.txt',
27+
]) {
28+
const content = `This is ${book}`;
29+
fs.writeFileSync(path.join(tmpdir.path, book), content, 'utf8');
30+
}
31+
fs.copyFileSync(entry, path.join(tmpdir.path, 'entry.js'));
2332
const child = spawnSync(process.execPath, [
2433
'--snapshot-blob',
2534
blobPath,
2635
'--build-snapshot',
27-
entry,
36+
'entry.js',
2837
], {
2938
cwd: tmpdir.path
3039
});
@@ -41,15 +50,18 @@ const entry = fixtures.path('snapshot', 'v8-startup-snapshot-api.js');
4150
const child = spawnSync(process.execPath, [
4251
'--snapshot-blob',
4352
blobPath,
53+
'book1',
4454
], {
4555
cwd: tmpdir.path,
4656
env: {
4757
...process.env,
58+
BOOK_LANG: 'en_US',
4859
}
4960
});
5061

5162
const stdout = child.stdout.toString().trim();
52-
const file = fs.readFileSync(fixtures.path('x1024.txt'), 'utf8');
53-
assert.strictEqual(stdout, file);
63+
const stderr = child.stderr.toString().trim();
64+
assert.strictEqual(stderr, 'Reading book1.en_US.txt');
65+
assert.strictEqual(stdout, 'This is book1.en_US.txt');
5466
assert.strictEqual(child.status, 0);
5567
}

0 commit comments

Comments
 (0)