Skip to content

Commit 811b43c

Browse files
joyeecheungtargos
authored andcommittedMay 12, 2023
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 <[email protected]>
1 parent f710676 commit 811b43c

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
@@ -921,15 +921,12 @@ added:
921921
> Stability: 1 - Experimental
922922
923923
The `v8.startupSnapshot` interface can be used to add serialization and
924-
deserialization hooks for custom startup snapshots. Currently the startup
925-
snapshots can only be built into the Node.js binary from source.
924+
deserialization hooks for custom startup snapshots.
926925

927926
```console
928-
$ cd /path/to/node
929-
$ ./configure --node-snapshot-main=entry.js
930-
$ make node
931-
# This binary contains the result of the execution of entry.js
932-
$ out/Release/node
927+
$ node --snapshot-blob snapshot.blob --build-snapshot entry.js
928+
# This launches a process with the snapshot
929+
$ node --snapshot-blob snapshot.blob
933930
```
934931

935932
In the example above, `entry.js` can use methods from the `v8.startupSnapshot`
@@ -946,42 +943,66 @@ const zlib = require('node:zlib');
946943
const path = require('node:path');
947944
const assert = require('node:assert');
948945

949-
const {
950-
isBuildingSnapshot,
951-
addSerializeCallback,
952-
addDeserializeCallback,
953-
setDeserializeMainFunction,
954-
} = require('node:v8').startupSnapshot;
946+
const v8 = require('node:v8');
955947

956-
const filePath = path.resolve(__dirname, '../x1024.txt');
957-
const storage = {};
948+
class BookShelf {
949+
storage = new Map();
958950

959-
assert(isBuildingSnapshot());
951+
// Reading a series of files from directory and store them into storage.
952+
constructor(directory, books) {
953+
for (const book of books) {
954+
this.storage.set(book, fs.readFileSync(path.join(directory, book)));
955+
}
956+
}
960957

961-
addSerializeCallback(({ filePath }) => {
962-
storage[filePath] = zlib.gzipSync(fs.readFileSync(filePath));
963-
}, { filePath });
958+
static compressAll(shelf) {
959+
for (const [ book, content ] of shelf.storage) {
960+
shelf.storage.set(book, zlib.gzipSync(content));
961+
}
962+
}
964963

965-
addDeserializeCallback(({ filePath }) => {
966-
storage[filePath] = zlib.gunzipSync(storage[filePath]);
967-
}, { filePath });
964+
static decompressAll(shelf) {
965+
for (const [ book, content ] of shelf.storage) {
966+
shelf.storage.set(book, zlib.gunzipSync(content));
967+
}
968+
}
969+
}
968970

969-
setDeserializeMainFunction(({ filePath }) => {
970-
console.log(storage[filePath].toString());
971-
}, { filePath });
971+
// __dirname here is where the snapshot script is placed
972+
// during snapshot building time.
973+
const shelf = new BookShelf(__dirname, [
974+
'book1.en_US.txt',
975+
'book1.es_ES.txt',
976+
'book2.zh_CN.txt',
977+
]);
978+
979+
assert(v8.startupSnapshot.isBuildingSnapshot());
980+
// On snapshot serialization, compress the books to reduce size.
981+
v8.startupSnapshot.addSerializeCallback(BookShelf.compressAll, shelf);
982+
// On snapshot deserialization, decompress the books.
983+
v8.startupSnapshot.addDeserializeCallback(BookShelf.decompressAll, shelf);
984+
v8.startupSnapshot.setDeserializeMainFunction((shelf) => {
985+
// process.env and process.argv are refreshed during snapshot
986+
// deserialization.
987+
const lang = process.env.BOOK_LANG || 'en_US';
988+
const book = process.argv[1];
989+
const name = `${book}.${lang}.txt`;
990+
console.log(shelf.storage.get(name));
991+
}, shelf);
972992
```
973993

974-
The resulted binary will simply print the data deserialized from the snapshot
975-
during start up:
994+
The resulted binary will get print the data deserialized from the snapshot
995+
during start up, using the refreshed `process.env` and `process.argv` of
996+
the launched process:
976997

977998
```console
978-
$ out/Release/node
979-
# Prints content of ./test/fixtures/x1024.txt
999+
$ BOOK_LANG=es_ES node --snapshot-blob snapshot.blob book1
1000+
# Prints content of book1.es_ES.txt deserialized from the snapshot.
9801001
```
9811002

982-
Currently the API is only available to a Node.js instance launched from the
983-
default snapshot, that is, the application deserialized from a user-land
984-
snapshot cannot use these APIs again.
1003+
Currently the application deserialized from a user-land snapshot cannot
1004+
be snapshotted again, so these APIs are only available to applications
1005+
that are not deserialized from a user-land snapshot.
9851006

9861007
### `v8.startupSnapshot.addSerializeCallback(callback[, data])`
9871008

‎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)
Please sign in to comment.