From 47dcb82a7486adc20f5dae4bf16031c09ec5e0c8 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Sat, 16 Jul 2022 08:22:57 +0200 Subject: [PATCH 01/10] doc(memory): draft - revamp using gc traces --- documentation/memory/step3/using_gc_traces.md | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 documentation/memory/step3/using_gc_traces.md diff --git a/documentation/memory/step3/using_gc_traces.md b/documentation/memory/step3/using_gc_traces.md new file mode 100644 index 0000000..91b1a71 --- /dev/null +++ b/documentation/memory/step3/using_gc_traces.md @@ -0,0 +1,283 @@ +# Tracing garbage collection + +In this guide you'll go through the fundamentals of garbage collection's traces. + +By the end of this guide you'll be able to: +* Enable traces in your Node.js program +* Interpret traces +* Find memory leak source + +There's probably a lot of stuff to learn about how the garbage collector works, but if you learn one thing it's that when GC is running, your code is not. + +You may want to know how often and how long the garbage collection is running. + +## Setup + +For the proposal of this guide, we will use a simple web server empowered with [Fastify](https://www.fastify.io/). + +1. Create a new project + +```bash +npm init -y +``` + +2. Install dependencies + +```bash +npm i fastify +``` + +3. Create a simple server (server.js) + +```js +import Fastify from 'fastify'; + +const server = Fastify(); +const entries = new Set(); + +server.get('/write', () => { + const date = new Date().toString(); + // don't do this at home + entries.add({ + date, + arch: os.arch(), + platform: os.platform(), + cpus: os.cpus() + }); + return true; +}); + +server.get('/read', () => { + return { count: entries.size() }; +}); + +server.listen(9999, (err, address) => { + if (err) { + console.error(err); + process.exit(1); + } + console.log(`server listening on ${address}`); +}); +``` + +> Even if the leak is evident here, in the context of a real-world application finding the source of a leak could be cumbersome. + +## Runnig with garbage collection traces +You can see traces for garbage collection in console output of your process using the `--trace_gc` flag. + +``` +node --trace_gc server.js +``` + +It should output something like: + +``` bash +[13973:0x110008000] 44 ms: Scavenge 2.4 (3.2) -> 2.0 (4.2) MB, 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure +[13973:0x110008000] 75 ms: Scavenge 2.7 (4.7) -> 2.4 (5.4) MB, 0.9 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure +[13973:0x110008000] 151 ms: Scavenge 3.9 (5.7) -> 3.4 (8.5) MB, 0.6 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure +[13973:0x110008000] 181 ms: Scavenge 5.3 (8.5) -> 4.3 (8.7) MB, 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure +[13973:0x110008000] 245 ms: Scavenge 6.8 (9.8) -> 5.9 (10.5) MB, 0.6 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure +[13973:0x110008000] 271 ms: Scavenge 7.5 (10.5) -> 6.4 (15.8) MB, 0.9 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure +server listening on http://127.0.0.1:9999 +``` + +Hard to read? Maybe we should pass in review a few concepts and explain the outputs of the `--trace-gc` flag. + +### Examining a trace with `--trace_gc` + +The `--trace-gc` flag outputs all garbage collection events in the console. First, let me describe the composition of each line. We'll use the following line as a model: + +```bash +[13973:0x110008000] 44 ms: Scavenge 2.4 (3.2) -> 2.0 (4.2) MB, 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure +``` + +| Token value | Interpretation | +|-------------------------------------------------------------|------------------------------------| +| 13973 | PID of the running process | +| 0x110008000 | Isolate (JS heap instance) | +| 44 ms | Time since the process start in ms | +| Scavenge | Type / Phase of GC | +| 2.4 | Heap used before GC in MB | +| (3.2) | Total heap before GC in MB | +| 2.0 | Heap used after GC in MB | +| (4.2) | Total heap after GC in MB | +| 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) | Time spent in GC in ms | +| allocation failure | Reason for GC | + +We'll only focus on two events here: +* Scavenge +* Mark-sweep + +The heap is divided into "spaces." Amongst these, we have a space called the "new" space and another one called the "old" space. + +> 👉 In reality, the heap is a bit different, but for the purpose of this article, we'll stick to a simpler version. If you want more details, I encourage you to look at this [talk of Peter Marshall](https://v8.dev/blog/trash-talk) about Orinoco. + +### Scavenge + +Scavenge is the name of an algorithm that will perform garbage collection into new space. +The new space is where objects are created. The new space is designed to be small and fast for garbage collection. + +Let's imagine a Scavenge scenario: + +* we allocated `A`, `B`, `C` & `D`. + ```bash + | A | B | C | D | | + ``` +* we want to allocate `E` +* not enough space, the memory is exhausted +* then, a (garbage) collection is triggered +* dead objects are collected +* living object will stay +* assuming `B` and `D` were dead + ```bash + | A | C | | + ``` +* now we can allocate `E` + ```bash + | A | C | E | | + ``` + +Objects that are not garbage collected after two Scavenge operations will be promoted to old space. + +> 👉 Full [Scavenge scenario](https://github.com/thlorenz/v8-perf/blob/master/gc.md#sample-scavenge-scenario). + +### Mark-sweep + +Mark-sweep is used to collect objects from old space. The old space is where objects that survived the new space are living. + +This algorithm is composed of two phases: +* **Mark**: Will mark still alive objects as black and others as white. +* **Sweep**: Scans for white objects and converts them to free spaces. + +> 👉 In fact, the Mark and Sweep steps are a bit more elaborate. Please read this [document](https://github.com/thlorenz/v8-perf/blob/master/gc.md#marking-state) for more details. + { + v8.setFlagsFromString('--trace_gc'); +}); + +server.get('/disable-gc-traces', ( + v8.setFlagsFromString('--notrace_gc'); +}); +``` + +### Using performance hooks + +For Node.js v8.5.0 or later, you can use [performance hooks](https://nodejs.org/api/perf_hooks.html) to trace garbage collection. + +```js +const { PerformanceObserver } = require('perf_hooks'); + +// Create a performance observer +const obs = new PerformanceObserver((list) => { + const entry = list.getEntries()[0] + /* + The entry would be an instance of PerformanceEntry containing + metrics of garbage collection. + For example: + PerformanceEntry { + name: 'gc', + entryType: 'gc', + name: 'gc', + duration: 1.315709, + kind: 1 + } + */ +}); + +// Subscribe notifications of GCs +obs.observe({ entryTypes: ['gc'] }); + +// Stop subscription +obs.disconnect(); +``` + +You can get GC statistics as [PerformanceEntry](https://nodejs.org/api/perf_hooks.html#perf_hooks_class_performanceentry) from the callback in [PerformanceObserver](https://nodejs.org/api/perf_hooks.html#perf_hooks_class_performanceobserver). + +For example: + +```js +PerformanceEntry { + name: 'gc', + entryType: 'gc', + startTime: 2820.567669, + duration: 1.315709, + kind: 1 +} +``` + +| Property | Interpretation | +|------------|----------------| +| name | The name of the performance entry. | +| entryType | The type of the performance entry. | +| startTime | The high resolution millisecond timestamp marking the starting time of the Performance Entry.| +| duration | The total number of milliseconds elapsed for this entry. | +| kind | The type of garbage collection operation that occurred. | +| flags | The high resolution millisecond timestamp marking the starting time of the Performance Entry.| + +For more information, you can refer to [the documentation about performance hooks](https://nodejs.org/api/perf_hooks.html). + From d9f6e12f36ce33a0182cddf724ff25959e618ef6 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Sat, 16 Jul 2022 15:24:55 +0200 Subject: [PATCH 02/10] doc(memory): fix typos --- documentation/memory/step3/using_gc_traces.md | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/documentation/memory/step3/using_gc_traces.md b/documentation/memory/step3/using_gc_traces.md index 91b1a71..38c8f1f 100644 --- a/documentation/memory/step3/using_gc_traces.md +++ b/documentation/memory/step3/using_gc_traces.md @@ -1,15 +1,15 @@ # Tracing garbage collection -In this guide you'll go through the fundamentals of garbage collection's traces. +This guide will go through the fundamentals of garbage collection traces. -By the end of this guide you'll be able to: -* Enable traces in your Node.js program +By the end of this guide, you'll be able to: +* Enable traces in your Node.js application * Interpret traces * Find memory leak source -There's probably a lot of stuff to learn about how the garbage collector works, but if you learn one thing it's that when GC is running, your code is not. +There's probably a lot of stuff to learn about how the garbage collector works, but if you have to know one thing, it's that when GC is running, your code is not. -You may want to know how often and how long the garbage collection is running. +You may want to know how often and long the garbage collection runs. ## Setup @@ -60,10 +60,10 @@ server.listen(9999, (err, address) => { }); ``` -> Even if the leak is evident here, in the context of a real-world application finding the source of a leak could be cumbersome. +> Even if the leak is evident here, finding the source of a leak could be cumbersome in the context of a real-world application. -## Runnig with garbage collection traces -You can see traces for garbage collection in console output of your process using the `--trace_gc` flag. +## Running with garbage collection traces +You can see traces for garbage collection in the console output of your process using the `--trace_gc` flag. ``` node --trace_gc server.js @@ -91,18 +91,18 @@ The `--trace-gc` flag outputs all garbage collection events in the console. Firs [13973:0x110008000] 44 ms: Scavenge 2.4 (3.2) -> 2.0 (4.2) MB, 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure ``` -| Token value | Interpretation | -|-------------------------------------------------------------|------------------------------------| -| 13973 | PID of the running process | -| 0x110008000 | Isolate (JS heap instance) | -| 44 ms | Time since the process start in ms | -| Scavenge | Type / Phase of GC | -| 2.4 | Heap used before GC in MB | -| (3.2) | Total heap before GC in MB | -| 2.0 | Heap used after GC in MB | -| (4.2) | Total heap after GC in MB | -| 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) | Time spent in GC in ms | -| allocation failure | Reason for GC | +| Token value | Interpretation | +|-------------------------------------------------------------|------------------------------------------| +| 13973 | PID of the running process | +| 0x110008000 | Isolate (JS heap instance) | +| 44 ms | The time since the process started in ms | +| Scavenge | Type / Phase of GC | +| 2.4 | Heap used before GC in MB | +| (3.2) | Total heap before GC in MB | +| 2.0 | Heap used after GC in MB | +| (4.2) | Total heap after GC in MB | +| 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) | Time spent in GC in ms | +| allocation failure | Reason for GC | We'll only focus on two events here: * Scavenge @@ -110,7 +110,7 @@ We'll only focus on two events here: The heap is divided into "spaces." Amongst these, we have a space called the "new" space and another one called the "old" space. -> 👉 In reality, the heap is a bit different, but for the purpose of this article, we'll stick to a simpler version. If you want more details, I encourage you to look at this [talk of Peter Marshall](https://v8.dev/blog/trash-talk) about Orinoco. +> 👉 In reality, the heap structure is a bit different, but we'll stick to a simpler version for this article. If you want more details, I encourage you to look at this [talk of Peter Marshall](https://v8.dev/blog/trash-talk) about Orinoco. ### Scavenge @@ -137,7 +137,7 @@ Let's imagine a Scavenge scenario: | A | C | E | | ``` -Objects that are not garbage collected after two Scavenge operations will be promoted to old space. +v8 will promote objects, not garbage collected after two Scavenge operations to the old space. > 👉 Full [Scavenge scenario](https://github.com/thlorenz/v8-perf/blob/master/gc.md#sample-scavenge-scenario). @@ -156,7 +156,7 @@ This algorithm is composed of two phases: ### Memory leak -Now we can come back to the output of the `--trace-gc` flag and see how we could interpret the output. +Now we can return to the output of the `--trace-gc` flag and see how we could interpret the result. * First, install (`autocannon`)[https://www.npmjs.com/package/autocannon]: ```bash @@ -173,7 +173,7 @@ node --trace-gc server.js autocannon http://localhost:9999/write ``` -Now, if you come back quickly to the previous terminal window: you will see that there are a lot of `Mark-sweep` events in the console. We also see that the amount of memory collected after the event is insignificant. +Now, if you return quickly to the previous terminal window: you will see many `Mark-sweep` events in the console. We also see that the amount of memory collected after the event is insignificant. Now that we are experts in garbage collection! What could we deduce? @@ -183,32 +183,32 @@ But how could we spot the context? ### How to get the context of bad allocations -We previously observes that the old space is continously increasing. The following steps will help you to know wich part of the code is reponsible. +We previously observed that the old space is continuously increasing. The following steps will help you to know which part of the code is responsible. - 1. Review the trace data and figure out how much is the total heap before and after the gc. - 2. Use the [`--max-old-space-size`](https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-megabytes) to reduce the total the old space size. - 3. Run the program, until you hit the out of memory. + 1. Review the trace data and figure out how much is the total heap before and after the GC. + 2. Use the [`--max-old-space-size`](https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-megabytes) to reduce the total old space size. + 3. Run the program until you hit the out of memory. 4. The produced log shows the failing context. - 6. If it hits OOM, increment the heap size by ~10% or so and repeat few times. If the same pattern is observed, it is indicative of a memory leak. - 7. If there is no OOM, then freeze the heap size to that value - A packed heap reduces memory footprint and compation latency. + 6. If it hits OOM, increment the heap size by ~10% and repeat a few times. If the same pattern is observed, it indicates a memory leak. + 7. If there is no OOM, then freeze the heap size to that value - A packed heap reduces memory footprint and computation latency. TODO(tony-go): add a snippet example ### Slowness -How to assert whether too many gcs are happening or too many gcs are causing an overhead? +How do you assert whether too many garbage collections are happening or causing an overhead? - 1. Review the trace data, specifically around time between consecutive gcs. - 2. Review the trace data, specifically around time spent in gc. - 3. If the time between two gc is less than the time spent in gc, the application is severely starving. - 4. If the time between two gcs and the time spent in gc are very high, probably the application can use a smaller heap. - 5. If the time between two gcs are much greater than the time spent in gc, application is relatively healthy. + 1. Review the trace data, precisely the time between consecutive collections. + 2. Review the trace data, specifically around time spent in GC. + 3. If the time between two GC is less than the time spent in GC, the application is severely starving. + 4. If the time between two GCS and the time spent in GC are very high, probably the application can use a smaller heap. + 5. If the time between two GCS is much greater than the time spent in GC, the application is relatively healthy. -## Bonnus: Trace garbage collection programaticaly +## Bonus: Trace garbage collection programmatically ### Using `v8` module -You might want to avoid getting traces from the entire lifetime of your process running on a server. In that case, set the flag from within the process. The `v8` module exposes an API to set flags on the fly. +You might want to avoid getting traces from the entire lifetime of your process running on a server. In that case, set the flag from within the process. The `v8` module exposes an API to put flags on the fly. ```js /// at the top of the 'server.js' file, please add: @@ -270,14 +270,14 @@ PerformanceEntry { } ``` -| Property | Interpretation | -|------------|----------------| -| name | The name of the performance entry. | -| entryType | The type of the performance entry. | -| startTime | The high resolution millisecond timestamp marking the starting time of the Performance Entry.| -| duration | The total number of milliseconds elapsed for this entry. | -| kind | The type of garbage collection operation that occurred. | -| flags | The high resolution millisecond timestamp marking the starting time of the Performance Entry.| +| Property | Interpretation | +|------------|-------------------------------------------------------------------------------------------------| +| name | The name of the performance entry. | +| entryType | The type of the performance entry. | +| startTime | The high-resolution millisecond timestamp is marking the starting time of the Performance Entry.| +| duration | The total number of milliseconds elapsed for this entry. | +| kind | The type of garbage collection operation that occurred. | +| flags | The high-resolution millisecond timestamp is marking the starting time of the Performance Entry.| -For more information, you can refer to [the documentation about performance hooks](https://nodejs.org/api/perf_hooks.html). +For more information, refer to [the documentation about performance hooks](https://nodejs.org/api/perf_hooks.html). From 02f69376e5be24079cebec76daa393df0d7b004c Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Sat, 16 Jul 2022 18:20:00 +0200 Subject: [PATCH 03/10] chore: add forgotten closing tag --- documentation/memory/step3/using_gc_traces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/memory/step3/using_gc_traces.md b/documentation/memory/step3/using_gc_traces.md index 38c8f1f..2e0b901 100644 --- a/documentation/memory/step3/using_gc_traces.md +++ b/documentation/memory/step3/using_gc_traces.md @@ -150,7 +150,7 @@ This algorithm is composed of two phases: * **Sweep**: Scans for white objects and converts them to free spaces. > 👉 In fact, the Mark and Sweep steps are a bit more elaborate. Please read this [document](https://github.com/thlorenz/v8-perf/blob/master/gc.md#marking-state) for more details. - ## `--trace-gc` in action From 1cc8ce49d5528ce9447f0295c6d5ab5990c6a963 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Mon, 18 Jul 2022 19:21:40 +0200 Subject: [PATCH 04/10] doc: add example folder --- .../memory/step3/exercice/.gitignore | 1 + .../memory/step3/exercice/package-lock.json | 835 ++++++++++++++++++ .../memory/step3/exercice/package.json | 23 + documentation/memory/step3/exercice/server.js | 52 ++ documentation/memory/step3/using_gc_traces.md | 4 +- 5 files changed, 912 insertions(+), 3 deletions(-) create mode 100644 documentation/memory/step3/exercice/.gitignore create mode 100644 documentation/memory/step3/exercice/package-lock.json create mode 100644 documentation/memory/step3/exercice/package.json create mode 100644 documentation/memory/step3/exercice/server.js diff --git a/documentation/memory/step3/exercice/.gitignore b/documentation/memory/step3/exercice/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/documentation/memory/step3/exercice/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/documentation/memory/step3/exercice/package-lock.json b/documentation/memory/step3/exercice/package-lock.json new file mode 100644 index 0000000..38eb2eb --- /dev/null +++ b/documentation/memory/step3/exercice/package-lock.json @@ -0,0 +1,835 @@ +{ + "name": "trace-gc-exercice", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "trace-gc-exercice", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "fastify": "^4.2.1" + } + }, + "node_modules/@fastify/ajv-compiler": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.1.2.tgz", + "integrity": "sha512-m2nzzQJeuVmeGOB9rnII9sZiY8AZ02a9WMQfMBfK1jxdFnxm3FPYKGbYpPjODj4halNogwpolyugbTNpnDCi0A==", + "dependencies": { + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "node_modules/@fastify/deepmerge": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.1.0.tgz", + "integrity": "sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==" + }, + "node_modules/@fastify/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", + "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.0.0.tgz", + "integrity": "sha512-9pCi6c6tmGt/qfuf2koZQuSIG6ckP9q3mz+JoMmAq9eQ4EtA92sWoK7E0LJUn2FFTS/hp5kag+4+dWsV5ZfcXg==", + "dependencies": { + "fast-json-stringify": "^5.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/avvio": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", + "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", + "dependencies": { + "archy": "^1.0.0", + "debug": "^4.0.0", + "fastq": "^1.6.1" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stringify": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.1.0.tgz", + "integrity": "sha512-IybGfbUc1DQgyrp9Myhwlr1Z5vjV37mBkdgcbuvsvUxv5fayG+cHlTQQpXH9nMwUPgp+5Y3RT7QDgx5zJ9NS3A==", + "dependencies": { + "@fastify/deepmerge": "^1.0.0", + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.1.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-redact": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.1.tgz", + "integrity": "sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-uri": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.1.0.tgz", + "integrity": "sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA==" + }, + "node_modules/fastify": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.2.1.tgz", + "integrity": "sha512-eyAWHN9+8IPTnhvGz+leseASGV/JZ75Y+jXXV7tid4awUjCMInY1gazZXuTD95xUW+Ve5vfgLjQ2i1i0/XJjdw==", + "dependencies": { + "@fastify/ajv-compiler": "^3.1.1", + "@fastify/error": "^3.0.0", + "@fastify/fast-json-stringify-compiler": "^4.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.1.3", + "find-my-way": "^7.0.0", + "light-my-request": "^5.0.0", + "pino": "^8.0.0", + "process-warning": "^2.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.4.0", + "semver": "^7.3.7", + "tiny-lru": "^8.0.2" + } + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/find-my-way": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.0.0.tgz", + "integrity": "sha512-NHVohYPYRXgj6jxXVRwm4iMQjA2ggJpyewHz7Nq7hvBnHoYJJIyHuxNzs8QLPTLQfoqxZzls2g6Zm79XMbhXjA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "safe-regex2": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/light-my-request": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.1.0.tgz", + "integrity": "sha512-pPQiXZaFGxVJccAC7zigUwyso5iCHz/sk17IKdcB6GUv6sXlxT7lOJFzosPhNs5/TyEXjITIEDfCyM3hHC34iQ==", + "dependencies": { + "cookie": "^0.5.0", + "process-warning": "^2.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", + "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + }, + "node_modules/pino": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.1.0.tgz", + "integrity": "sha512-53jlxs+02UNTtF1XwVWfa0dHipBiM5GK73XhkHn8M2hUl9y3L94dNwB8BwQhpd5WdHjBkyJiO7v0LRt4SGgsPg==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.0.0", + "pino-std-serializers": "^5.0.0", + "process-warning": "^2.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.1.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.0.0", + "thread-stream": "^1.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", + "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-5.6.0.tgz", + "integrity": "sha512-VdUXCw8gO+xhir7sFuoYSjTnzB+TMDGxhAC/ph3YS3sdHnXNdsK0wMtADNUltfeGkn2KDxEM21fnjF3RwXyC8A==" + }, + "node_modules/process-warning": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", + "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "node_modules/readable-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.1.0.tgz", + "integrity": "sha512-sVisi3+P2lJ2t0BPbpK629j8wRW06yKGJUcaLAGXPAUhyUxVJm7VsCTit1PFgT4JHUDMrGNR+ZjSKpzGaRF3zw==", + "dependencies": { + "abort-controller": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/real-require": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", + "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, + "node_modules/safe-regex2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "dependencies": { + "ret": "~0.2.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/secure-json-parse": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz", + "integrity": "sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==" + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.0.tgz", + "integrity": "sha512-cHMAtSXilfyBePduZEBVPTCftTQWz6ehWJD5YNUg4mqvRosrrjKbo4WS8JkB0/RxonMoohHm7cOGH60mDkRQ9w==" + }, + "node_modules/sonic-boom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.0.0.tgz", + "integrity": "sha512-p5DiZOZHbJ2ZO5MADczp5qrfOd3W5Vr2vHxfCpe7G4AzPwVOweIjbfgku8wSQUuk+Y5Yuo8W7JqRe6XKmKistg==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/thread-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-1.0.1.tgz", + "integrity": "sha512-JuZyfzx81e5MBk8uIr8ZH76bXyjEQvbRDEkSdlV1JFBdq/rbby2RuvzBYlTBd/xCljxy6lPxrTLXzB9Jl1bNrw==", + "dependencies": { + "real-require": "^0.1.0" + } + }, + "node_modules/tiny-lru": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", + "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "@fastify/ajv-compiler": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.1.2.tgz", + "integrity": "sha512-m2nzzQJeuVmeGOB9rnII9sZiY8AZ02a9WMQfMBfK1jxdFnxm3FPYKGbYpPjODj4halNogwpolyugbTNpnDCi0A==", + "requires": { + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "@fastify/deepmerge": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.1.0.tgz", + "integrity": "sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==" + }, + "@fastify/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", + "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" + }, + "@fastify/fast-json-stringify-compiler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.0.0.tgz", + "integrity": "sha512-9pCi6c6tmGt/qfuf2koZQuSIG6ckP9q3mz+JoMmAq9eQ4EtA92sWoK7E0LJUn2FFTS/hp5kag+4+dWsV5ZfcXg==", + "requires": { + "fast-json-stringify": "^5.0.0" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" + }, + "atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + }, + "avvio": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", + "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", + "requires": { + "archy": "^1.0.0", + "debug": "^4.0.0", + "fastq": "^1.6.1" + } + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stringify": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.1.0.tgz", + "integrity": "sha512-IybGfbUc1DQgyrp9Myhwlr1Z5vjV37mBkdgcbuvsvUxv5fayG+cHlTQQpXH9nMwUPgp+5Y3RT7QDgx5zJ9NS3A==", + "requires": { + "@fastify/deepmerge": "^1.0.0", + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.1.0", + "rfdc": "^1.2.0" + } + }, + "fast-redact": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.1.tgz", + "integrity": "sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A==" + }, + "fast-uri": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.1.0.tgz", + "integrity": "sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA==" + }, + "fastify": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.2.1.tgz", + "integrity": "sha512-eyAWHN9+8IPTnhvGz+leseASGV/JZ75Y+jXXV7tid4awUjCMInY1gazZXuTD95xUW+Ve5vfgLjQ2i1i0/XJjdw==", + "requires": { + "@fastify/ajv-compiler": "^3.1.1", + "@fastify/error": "^3.0.0", + "@fastify/fast-json-stringify-compiler": "^4.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.1.3", + "find-my-way": "^7.0.0", + "light-my-request": "^5.0.0", + "pino": "^8.0.0", + "process-warning": "^2.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.4.0", + "semver": "^7.3.7", + "tiny-lru": "^8.0.2" + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "find-my-way": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.0.0.tgz", + "integrity": "sha512-NHVohYPYRXgj6jxXVRwm4iMQjA2ggJpyewHz7Nq7hvBnHoYJJIyHuxNzs8QLPTLQfoqxZzls2g6Zm79XMbhXjA==", + "requires": { + "fast-deep-equal": "^3.1.3", + "safe-regex2": "^2.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "light-my-request": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.1.0.tgz", + "integrity": "sha512-pPQiXZaFGxVJccAC7zigUwyso5iCHz/sk17IKdcB6GUv6sXlxT7lOJFzosPhNs5/TyEXjITIEDfCyM3hHC34iQ==", + "requires": { + "cookie": "^0.5.0", + "process-warning": "^2.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "on-exit-leak-free": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", + "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + }, + "pino": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.1.0.tgz", + "integrity": "sha512-53jlxs+02UNTtF1XwVWfa0dHipBiM5GK73XhkHn8M2hUl9y3L94dNwB8BwQhpd5WdHjBkyJiO7v0LRt4SGgsPg==", + "requires": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.0.0", + "pino-std-serializers": "^5.0.0", + "process-warning": "^2.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.1.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.0.0", + "thread-stream": "^1.0.0" + } + }, + "pino-abstract-transport": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", + "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", + "requires": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "pino-std-serializers": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-5.6.0.tgz", + "integrity": "sha512-VdUXCw8gO+xhir7sFuoYSjTnzB+TMDGxhAC/ph3YS3sdHnXNdsK0wMtADNUltfeGkn2KDxEM21fnjF3RwXyC8A==" + }, + "process-warning": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", + "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "readable-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.1.0.tgz", + "integrity": "sha512-sVisi3+P2lJ2t0BPbpK629j8wRW06yKGJUcaLAGXPAUhyUxVJm7VsCTit1PFgT4JHUDMrGNR+ZjSKpzGaRF3zw==", + "requires": { + "abort-controller": "^3.0.0" + } + }, + "real-require": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", + "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, + "safe-regex2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "requires": { + "ret": "~0.2.0" + } + }, + "safe-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==" + }, + "secure-json-parse": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz", + "integrity": "sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==" + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-cookie-parser": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.0.tgz", + "integrity": "sha512-cHMAtSXilfyBePduZEBVPTCftTQWz6ehWJD5YNUg4mqvRosrrjKbo4WS8JkB0/RxonMoohHm7cOGH60mDkRQ9w==" + }, + "sonic-boom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.0.0.tgz", + "integrity": "sha512-p5DiZOZHbJ2ZO5MADczp5qrfOd3W5Vr2vHxfCpe7G4AzPwVOweIjbfgku8wSQUuk+Y5Yuo8W7JqRe6XKmKistg==", + "requires": { + "atomic-sleep": "^1.0.0" + } + }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, + "thread-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-1.0.1.tgz", + "integrity": "sha512-JuZyfzx81e5MBk8uIr8ZH76bXyjEQvbRDEkSdlV1JFBdq/rbby2RuvzBYlTBd/xCljxy6lPxrTLXzB9Jl1bNrw==", + "requires": { + "real-require": "^0.1.0" + } + }, + "tiny-lru": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", + "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/documentation/memory/step3/exercice/package.json b/documentation/memory/step3/exercice/package.json new file mode 100644 index 0000000..71925f2 --- /dev/null +++ b/documentation/memory/step3/exercice/package.json @@ -0,0 +1,23 @@ +{ + "name": "trace-gc-exercice", + "version": "0.0.1", + "description": "Exercice for the trace-gc documentation", + "main": "server.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "keywords": [ + "trace-gc", + "v8", + "node", + "diagnostic", + "heap" + ], + "author": "Tony Gorez ", + "license": "MIT", + "dependencies": { + "fastify": "^4.2.1" + } +} diff --git a/documentation/memory/step3/exercice/server.js b/documentation/memory/step3/exercice/server.js new file mode 100644 index 0000000..6326274 --- /dev/null +++ b/documentation/memory/step3/exercice/server.js @@ -0,0 +1,52 @@ +// node.js dependencies +import v8 from 'v8'; +import os from 'os'; + +// third-party dependencies +import Fastify from 'fastify'; + +// constants +const server = Fastify(); +const entries = new Set(); + +server.get('/write', () => { + const date = new Date().toString(); + // don't do this at home + entries.add({ + date, + arch: os.arch(), + platform: os.platform(), + cpus: os.cpus() + }); + + return true; +}); + +server.get('/read', () => { + return { count: entries.size }; +}); + +/** + * + * Diagnostic helpers endpoints + * + */ + +server.get('/enable-gc-traces', () => { + v8.setFlagsFromString('--trace_gc'); + return true; +}); + +server.get('/disable-gc-traces', () => { + v8.setFlagsFromString('--notrace_gc'); + return true; +}); + + +server.listen(9999, (err, address) => { + if (err) { + console.error(err); + process.exit(1); + } + console.log(`server listening on ${address}`); +}); diff --git a/documentation/memory/step3/using_gc_traces.md b/documentation/memory/step3/using_gc_traces.md index 2e0b901..c1e36ae 100644 --- a/documentation/memory/step3/using_gc_traces.md +++ b/documentation/memory/step3/using_gc_traces.md @@ -192,8 +192,6 @@ We previously observed that the old space is continuously increasing. The follow 6. If it hits OOM, increment the heap size by ~10% and repeat a few times. If the same pattern is observed, it indicates a memory leak. 7. If there is no OOM, then freeze the heap size to that value - A packed heap reduces memory footprint and computation latency. -TODO(tony-go): add a snippet example - ### Slowness How do you assert whether too many garbage collections are happening or causing an overhead? @@ -220,7 +218,7 @@ server.get('/enable-gc-traces', () => { v8.setFlagsFromString('--trace_gc'); }); -server.get('/disable-gc-traces', ( +server.get('/disable-gc-traces', () => { v8.setFlagsFromString('--notrace_gc'); }); ``` From 9d518f8aed9d8c1792c7728c8d6278706babfa53 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Mon, 8 Aug 2022 18:17:07 +0200 Subject: [PATCH 05/10] doc: apply review suggestions --- documentation/memory/step3/using_gc_traces.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/memory/step3/using_gc_traces.md b/documentation/memory/step3/using_gc_traces.md index c1e36ae..e7f02de 100644 --- a/documentation/memory/step3/using_gc_traces.md +++ b/documentation/memory/step3/using_gc_traces.md @@ -5,7 +5,7 @@ This guide will go through the fundamentals of garbage collection traces. By the end of this guide, you'll be able to: * Enable traces in your Node.js application * Interpret traces -* Find memory leak source +* Identify potential memory issues in your Node.js application There's probably a lot of stuff to learn about how the garbage collector works, but if you have to know one thing, it's that when GC is running, your code is not. @@ -63,7 +63,7 @@ server.listen(9999, (err, address) => { > Even if the leak is evident here, finding the source of a leak could be cumbersome in the context of a real-world application. ## Running with garbage collection traces -You can see traces for garbage collection in the console output of your process using the `--trace_gc` flag. +You can see traces for garbage collection in the standard output of your process using the `--trace_gc` flag. ``` node --trace_gc server.js @@ -85,7 +85,7 @@ Hard to read? Maybe we should pass in review a few concepts and explain the outp ### Examining a trace with `--trace_gc` -The `--trace-gc` flag outputs all garbage collection events in the console. First, let me describe the composition of each line. We'll use the following line as a model: +The `--trace-gc` flag outputs all garbage collection events in the console. The composition of each line can be described as: ```bash [13973:0x110008000] 44 ms: Scavenge 2.4 (3.2) -> 2.0 (4.2) MB, 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure @@ -108,7 +108,7 @@ We'll only focus on two events here: * Scavenge * Mark-sweep -The heap is divided into "spaces." Amongst these, we have a space called the "new" space and another one called the "old" space. +The heap is divided into _spaces_. Amongst these, we have a space called the "new" space and another one called the "old" space. > 👉 In reality, the heap structure is a bit different, but we'll stick to a simpler version for this article. If you want more details, I encourage you to look at this [talk of Peter Marshall](https://v8.dev/blog/trash-talk) about Orinoco. From 50f807ee7cedd6b62bee6ad0c5edfbbd51ac3df3 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Fri, 12 Aug 2022 17:58:01 +0200 Subject: [PATCH 06/10] wip: add script alternative --- documentation/memory/step3/exercice/script.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 documentation/memory/step3/exercice/script.js diff --git a/documentation/memory/step3/exercice/script.js b/documentation/memory/step3/exercice/script.js new file mode 100644 index 0000000..68f34ab --- /dev/null +++ b/documentation/memory/step3/exercice/script.js @@ -0,0 +1,45 @@ +// node.js dependecies +import os from 'os'; +import { createHmac } from 'crypto'; +import fs from 'fs/promises'; + +// constants +let len = 1_000_000; +const entries = new Set(); +// fix: const fileName = `entries-${Date.now()}`; + +async function addEntry () { + const secret = 'abcdefg'; + const hash = createHmac('sha256', secret) + .update('I love cupcakes') + .digest('hex'); + + const entry = { + timestamp: Date.now(), + memory: os.freemem(), + totalMemory: os.totalmem(), + uptime: os.uptime(), + hash + }; + + entries.add(entry); + // fix: await fs.appendFile(fileName, JSON.stringify(entry)); +} + +function summary () { + console.log(`Total: ${entries.size} entries`); + // fix: console.log('end'); +} + +// execution +(async () => { + await fs.writeFile(fileName, "start"); + while (len > 0) { + await addEntry(); + process.stdout.write(`~~> ${len} entries to record\r`); + len--; + }; + + setImmediate(summary); +})(); + From 431552ec45c85f04d50ba501a884e31db9f4d296 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Tue, 16 Aug 2022 17:22:45 +0200 Subject: [PATCH 07/10] doc: move trace-gc doc to script --- .../memory/step3/exercice/.gitignore | 1 - .../memory/step3/exercice/package-lock.json | 835 ------------------ .../memory/step3/exercice/package.json | 23 - .../memory/step3/exercice/script-fix.mjs | 34 + documentation/memory/step3/exercice/script.js | 45 - .../memory/step3/exercice/script.mjs | 31 + documentation/memory/step3/exercice/server.js | 52 -- documentation/memory/step3/using_gc_traces.md | 222 +++-- 8 files changed, 208 insertions(+), 1035 deletions(-) delete mode 100644 documentation/memory/step3/exercice/.gitignore delete mode 100644 documentation/memory/step3/exercice/package-lock.json delete mode 100644 documentation/memory/step3/exercice/package.json create mode 100644 documentation/memory/step3/exercice/script-fix.mjs delete mode 100644 documentation/memory/step3/exercice/script.js create mode 100644 documentation/memory/step3/exercice/script.mjs delete mode 100644 documentation/memory/step3/exercice/server.js diff --git a/documentation/memory/step3/exercice/.gitignore b/documentation/memory/step3/exercice/.gitignore deleted file mode 100644 index 3c3629e..0000000 --- a/documentation/memory/step3/exercice/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/documentation/memory/step3/exercice/package-lock.json b/documentation/memory/step3/exercice/package-lock.json deleted file mode 100644 index 38eb2eb..0000000 --- a/documentation/memory/step3/exercice/package-lock.json +++ /dev/null @@ -1,835 +0,0 @@ -{ - "name": "trace-gc-exercice", - "version": "0.0.1", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "trace-gc-exercice", - "version": "0.0.1", - "license": "MIT", - "dependencies": { - "fastify": "^4.2.1" - } - }, - "node_modules/@fastify/ajv-compiler": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.1.2.tgz", - "integrity": "sha512-m2nzzQJeuVmeGOB9rnII9sZiY8AZ02a9WMQfMBfK1jxdFnxm3FPYKGbYpPjODj4halNogwpolyugbTNpnDCi0A==", - "dependencies": { - "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" - } - }, - "node_modules/@fastify/deepmerge": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.1.0.tgz", - "integrity": "sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==" - }, - "node_modules/@fastify/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", - "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" - }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.0.0.tgz", - "integrity": "sha512-9pCi6c6tmGt/qfuf2koZQuSIG6ckP9q3mz+JoMmAq9eQ4EtA92sWoK7E0LJUn2FFTS/hp5kag+4+dWsV5ZfcXg==", - "dependencies": { - "fast-json-stringify": "^5.0.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" - }, - "node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/avvio": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", - "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", - "dependencies": { - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.6.1" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-json-stringify": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.1.0.tgz", - "integrity": "sha512-IybGfbUc1DQgyrp9Myhwlr1Z5vjV37mBkdgcbuvsvUxv5fayG+cHlTQQpXH9nMwUPgp+5Y3RT7QDgx5zJ9NS3A==", - "dependencies": { - "@fastify/deepmerge": "^1.0.0", - "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.1.0", - "rfdc": "^1.2.0" - } - }, - "node_modules/fast-redact": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.1.tgz", - "integrity": "sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-uri": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.1.0.tgz", - "integrity": "sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA==" - }, - "node_modules/fastify": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.2.1.tgz", - "integrity": "sha512-eyAWHN9+8IPTnhvGz+leseASGV/JZ75Y+jXXV7tid4awUjCMInY1gazZXuTD95xUW+Ve5vfgLjQ2i1i0/XJjdw==", - "dependencies": { - "@fastify/ajv-compiler": "^3.1.1", - "@fastify/error": "^3.0.0", - "@fastify/fast-json-stringify-compiler": "^4.0.0", - "abstract-logging": "^2.0.1", - "avvio": "^8.1.3", - "find-my-way": "^7.0.0", - "light-my-request": "^5.0.0", - "pino": "^8.0.0", - "process-warning": "^2.0.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.4.0", - "semver": "^7.3.7", - "tiny-lru": "^8.0.2" - } - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/find-my-way": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.0.0.tgz", - "integrity": "sha512-NHVohYPYRXgj6jxXVRwm4iMQjA2ggJpyewHz7Nq7hvBnHoYJJIyHuxNzs8QLPTLQfoqxZzls2g6Zm79XMbhXjA==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "safe-regex2": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/light-my-request": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.1.0.tgz", - "integrity": "sha512-pPQiXZaFGxVJccAC7zigUwyso5iCHz/sk17IKdcB6GUv6sXlxT7lOJFzosPhNs5/TyEXjITIEDfCyM3hHC34iQ==", - "dependencies": { - "cookie": "^0.5.0", - "process-warning": "^2.0.0", - "set-cookie-parser": "^2.4.1" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" - }, - "node_modules/pino": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.1.0.tgz", - "integrity": "sha512-53jlxs+02UNTtF1XwVWfa0dHipBiM5GK73XhkHn8M2hUl9y3L94dNwB8BwQhpd5WdHjBkyJiO7v0LRt4SGgsPg==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.0.0", - "pino-std-serializers": "^5.0.0", - "process-warning": "^2.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.1.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.0.0", - "thread-stream": "^1.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", - "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", - "dependencies": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-5.6.0.tgz", - "integrity": "sha512-VdUXCw8gO+xhir7sFuoYSjTnzB+TMDGxhAC/ph3YS3sdHnXNdsK0wMtADNUltfeGkn2KDxEM21fnjF3RwXyC8A==" - }, - "node_modules/process-warning": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", - "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" - }, - "node_modules/readable-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.1.0.tgz", - "integrity": "sha512-sVisi3+P2lJ2t0BPbpK629j8wRW06yKGJUcaLAGXPAUhyUxVJm7VsCTit1PFgT4JHUDMrGNR+ZjSKpzGaRF3zw==", - "dependencies": { - "abort-controller": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/real-require": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", - "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" - }, - "node_modules/safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", - "dependencies": { - "ret": "~0.2.0" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", - "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", - "engines": { - "node": ">=10" - } - }, - "node_modules/secure-json-parse": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz", - "integrity": "sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==" - }, - "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.0.tgz", - "integrity": "sha512-cHMAtSXilfyBePduZEBVPTCftTQWz6ehWJD5YNUg4mqvRosrrjKbo4WS8JkB0/RxonMoohHm7cOGH60mDkRQ9w==" - }, - "node_modules/sonic-boom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.0.0.tgz", - "integrity": "sha512-p5DiZOZHbJ2ZO5MADczp5qrfOd3W5Vr2vHxfCpe7G4AzPwVOweIjbfgku8wSQUuk+Y5Yuo8W7JqRe6XKmKistg==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/split2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", - "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/thread-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-1.0.1.tgz", - "integrity": "sha512-JuZyfzx81e5MBk8uIr8ZH76bXyjEQvbRDEkSdlV1JFBdq/rbby2RuvzBYlTBd/xCljxy6lPxrTLXzB9Jl1bNrw==", - "dependencies": { - "real-require": "^0.1.0" - } - }, - "node_modules/tiny-lru": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", - "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - }, - "dependencies": { - "@fastify/ajv-compiler": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.1.2.tgz", - "integrity": "sha512-m2nzzQJeuVmeGOB9rnII9sZiY8AZ02a9WMQfMBfK1jxdFnxm3FPYKGbYpPjODj4halNogwpolyugbTNpnDCi0A==", - "requires": { - "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" - } - }, - "@fastify/deepmerge": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.1.0.tgz", - "integrity": "sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==" - }, - "@fastify/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", - "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" - }, - "@fastify/fast-json-stringify-compiler": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.0.0.tgz", - "integrity": "sha512-9pCi6c6tmGt/qfuf2koZQuSIG6ckP9q3mz+JoMmAq9eQ4EtA92sWoK7E0LJUn2FFTS/hp5kag+4+dWsV5ZfcXg==", - "requires": { - "fast-json-stringify": "^5.0.0" - } - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" - }, - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": { - "ajv": "^8.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, - "atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" - }, - "avvio": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", - "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", - "requires": { - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.6.1" - } - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stringify": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.1.0.tgz", - "integrity": "sha512-IybGfbUc1DQgyrp9Myhwlr1Z5vjV37mBkdgcbuvsvUxv5fayG+cHlTQQpXH9nMwUPgp+5Y3RT7QDgx5zJ9NS3A==", - "requires": { - "@fastify/deepmerge": "^1.0.0", - "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.1.0", - "rfdc": "^1.2.0" - } - }, - "fast-redact": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.1.tgz", - "integrity": "sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A==" - }, - "fast-uri": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.1.0.tgz", - "integrity": "sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA==" - }, - "fastify": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.2.1.tgz", - "integrity": "sha512-eyAWHN9+8IPTnhvGz+leseASGV/JZ75Y+jXXV7tid4awUjCMInY1gazZXuTD95xUW+Ve5vfgLjQ2i1i0/XJjdw==", - "requires": { - "@fastify/ajv-compiler": "^3.1.1", - "@fastify/error": "^3.0.0", - "@fastify/fast-json-stringify-compiler": "^4.0.0", - "abstract-logging": "^2.0.1", - "avvio": "^8.1.3", - "find-my-way": "^7.0.0", - "light-my-request": "^5.0.0", - "pino": "^8.0.0", - "process-warning": "^2.0.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.4.0", - "semver": "^7.3.7", - "tiny-lru": "^8.0.2" - } - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "requires": { - "reusify": "^1.0.4" - } - }, - "find-my-way": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.0.0.tgz", - "integrity": "sha512-NHVohYPYRXgj6jxXVRwm4iMQjA2ggJpyewHz7Nq7hvBnHoYJJIyHuxNzs8QLPTLQfoqxZzls2g6Zm79XMbhXjA==", - "requires": { - "fast-deep-equal": "^3.1.3", - "safe-regex2": "^2.0.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "light-my-request": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.1.0.tgz", - "integrity": "sha512-pPQiXZaFGxVJccAC7zigUwyso5iCHz/sk17IKdcB6GUv6sXlxT7lOJFzosPhNs5/TyEXjITIEDfCyM3hHC34iQ==", - "requires": { - "cookie": "^0.5.0", - "process-warning": "^2.0.0", - "set-cookie-parser": "^2.4.1" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" - }, - "pino": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.1.0.tgz", - "integrity": "sha512-53jlxs+02UNTtF1XwVWfa0dHipBiM5GK73XhkHn8M2hUl9y3L94dNwB8BwQhpd5WdHjBkyJiO7v0LRt4SGgsPg==", - "requires": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.0.0", - "pino-std-serializers": "^5.0.0", - "process-warning": "^2.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.1.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.0.0", - "thread-stream": "^1.0.0" - } - }, - "pino-abstract-transport": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", - "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", - "requires": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - } - }, - "pino-std-serializers": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-5.6.0.tgz", - "integrity": "sha512-VdUXCw8gO+xhir7sFuoYSjTnzB+TMDGxhAC/ph3YS3sdHnXNdsK0wMtADNUltfeGkn2KDxEM21fnjF3RwXyC8A==" - }, - "process-warning": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", - "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" - }, - "readable-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.1.0.tgz", - "integrity": "sha512-sVisi3+P2lJ2t0BPbpK629j8wRW06yKGJUcaLAGXPAUhyUxVJm7VsCTit1PFgT4JHUDMrGNR+ZjSKpzGaRF3zw==", - "requires": { - "abort-controller": "^3.0.0" - } - }, - "real-require": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", - "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==" - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, - "ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" - }, - "safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", - "requires": { - "ret": "~0.2.0" - } - }, - "safe-stable-stringify": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", - "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==" - }, - "secure-json-parse": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.4.0.tgz", - "integrity": "sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==" - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "set-cookie-parser": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.0.tgz", - "integrity": "sha512-cHMAtSXilfyBePduZEBVPTCftTQWz6ehWJD5YNUg4mqvRosrrjKbo4WS8JkB0/RxonMoohHm7cOGH60mDkRQ9w==" - }, - "sonic-boom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.0.0.tgz", - "integrity": "sha512-p5DiZOZHbJ2ZO5MADczp5qrfOd3W5Vr2vHxfCpe7G4AzPwVOweIjbfgku8wSQUuk+Y5Yuo8W7JqRe6XKmKistg==", - "requires": { - "atomic-sleep": "^1.0.0" - } - }, - "split2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", - "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" - }, - "thread-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-1.0.1.tgz", - "integrity": "sha512-JuZyfzx81e5MBk8uIr8ZH76bXyjEQvbRDEkSdlV1JFBdq/rbby2RuvzBYlTBd/xCljxy6lPxrTLXzB9Jl1bNrw==", - "requires": { - "real-require": "^0.1.0" - } - }, - "tiny-lru": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", - "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } -} diff --git a/documentation/memory/step3/exercice/package.json b/documentation/memory/step3/exercice/package.json deleted file mode 100644 index 71925f2..0000000 --- a/documentation/memory/step3/exercice/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "trace-gc-exercice", - "version": "0.0.1", - "description": "Exercice for the trace-gc documentation", - "main": "server.js", - "type": "module", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node server.js" - }, - "keywords": [ - "trace-gc", - "v8", - "node", - "diagnostic", - "heap" - ], - "author": "Tony Gorez ", - "license": "MIT", - "dependencies": { - "fastify": "^4.2.1" - } -} diff --git a/documentation/memory/step3/exercice/script-fix.mjs b/documentation/memory/step3/exercice/script-fix.mjs new file mode 100644 index 0000000..df7156f --- /dev/null +++ b/documentation/memory/step3/exercice/script-fix.mjs @@ -0,0 +1,34 @@ +import os from 'os'; +import fs from 'fs/promises'; + +let len = 1_000_000; +const fileName = `entries-${Date.now()}`; + +async function addEntry () { + const entry = { + timestamp: Date.now(), + memory: os.freemem(), + totalMemory: os.totalmem(), + uptime: os.uptime(), + }; + + await fs.appendFile(fileName, JSON.stringify(entry) + '\n'); +} + +async function summary () { + const stats = await fs.lstat(fileName); + console.log(`File size: ${stats.size} bytes! \n`); +} + +// execution +(async () => { + await fs.writeFile(fileName, "----START---\n"); + while (len > 0) { + await addEntry(); + process.stdout.write(`~~> ${len} entries to record\r`); + len--; + }; + + await summary(); +})(); + diff --git a/documentation/memory/step3/exercice/script.js b/documentation/memory/step3/exercice/script.js deleted file mode 100644 index 68f34ab..0000000 --- a/documentation/memory/step3/exercice/script.js +++ /dev/null @@ -1,45 +0,0 @@ -// node.js dependecies -import os from 'os'; -import { createHmac } from 'crypto'; -import fs from 'fs/promises'; - -// constants -let len = 1_000_000; -const entries = new Set(); -// fix: const fileName = `entries-${Date.now()}`; - -async function addEntry () { - const secret = 'abcdefg'; - const hash = createHmac('sha256', secret) - .update('I love cupcakes') - .digest('hex'); - - const entry = { - timestamp: Date.now(), - memory: os.freemem(), - totalMemory: os.totalmem(), - uptime: os.uptime(), - hash - }; - - entries.add(entry); - // fix: await fs.appendFile(fileName, JSON.stringify(entry)); -} - -function summary () { - console.log(`Total: ${entries.size} entries`); - // fix: console.log('end'); -} - -// execution -(async () => { - await fs.writeFile(fileName, "start"); - while (len > 0) { - await addEntry(); - process.stdout.write(`~~> ${len} entries to record\r`); - len--; - }; - - setImmediate(summary); -})(); - diff --git a/documentation/memory/step3/exercice/script.mjs b/documentation/memory/step3/exercice/script.mjs new file mode 100644 index 0000000..02ba922 --- /dev/null +++ b/documentation/memory/step3/exercice/script.mjs @@ -0,0 +1,31 @@ +import os from 'os'; + +let len = 1_000_000; +const entries = new Set(); + +function addEntry () { + const entry = { + timestamp: Date.now(), + memory: os.freemem(), + totalMemory: os.totalmem(), + uptime: os.uptime(), + }; + + entries.add(entry); +} + +function summary () { + console.log(`Total: ${entries.size} entries`); +} + +// execution +(() => { + while (len > 0) { + addEntry(); + process.stdout.write(`~~> ${len} entries to record\r`); + len--; + }; + + summary(); +})(); + diff --git a/documentation/memory/step3/exercice/server.js b/documentation/memory/step3/exercice/server.js deleted file mode 100644 index 6326274..0000000 --- a/documentation/memory/step3/exercice/server.js +++ /dev/null @@ -1,52 +0,0 @@ -// node.js dependencies -import v8 from 'v8'; -import os from 'os'; - -// third-party dependencies -import Fastify from 'fastify'; - -// constants -const server = Fastify(); -const entries = new Set(); - -server.get('/write', () => { - const date = new Date().toString(); - // don't do this at home - entries.add({ - date, - arch: os.arch(), - platform: os.platform(), - cpus: os.cpus() - }); - - return true; -}); - -server.get('/read', () => { - return { count: entries.size }; -}); - -/** - * - * Diagnostic helpers endpoints - * - */ - -server.get('/enable-gc-traces', () => { - v8.setFlagsFromString('--trace_gc'); - return true; -}); - -server.get('/disable-gc-traces', () => { - v8.setFlagsFromString('--notrace_gc'); - return true; -}); - - -server.listen(9999, (err, address) => { - if (err) { - console.error(err); - process.exit(1); - } - console.log(`server listening on ${address}`); -}); diff --git a/documentation/memory/step3/using_gc_traces.md b/documentation/memory/step3/using_gc_traces.md index e7f02de..7d447e3 100644 --- a/documentation/memory/step3/using_gc_traces.md +++ b/documentation/memory/step3/using_gc_traces.md @@ -13,51 +13,40 @@ You may want to know how often and long the garbage collection runs. ## Setup -For the proposal of this guide, we will use a simple web server empowered with [Fastify](https://www.fastify.io/). - -1. Create a new project - -```bash -npm init -y -``` - -2. Install dependencies - -```bash -npm i fastify -``` - -3. Create a simple server (server.js) +For the proposal of this guide, we'll use this script: ```js -import Fastify from 'fastify'; +// script.js +import os from 'os'; -const server = Fastify(); +let len = 1_000_000; const entries = new Set(); -server.get('/write', () => { - const date = new Date().toString(); - // don't do this at home - entries.add({ - date, - arch: os.arch(), - platform: os.platform(), - cpus: os.cpus() - }); - return true; -}); +function addEntry () { + const entry = { + timestamp: Date.now(), + memory: os.freemem(), + totalMemory: os.totalmem(), + uptime: os.uptime(), + }; -server.get('/read', () => { - return { count: entries.size() }; -}); + entries.add(entry); +} -server.listen(9999, (err, address) => { - if (err) { - console.error(err); - process.exit(1); - } - console.log(`server listening on ${address}`); -}); +function summary () { + console.log(`Total: ${entries.size} entries`); +} + +// execution +(() => { + while (len > 0) { + addEntry(); + process.stdout.write(`~~> ${len} entries to record\r`); + len--; + }; + + summary(); +})(); ``` > Even if the leak is evident here, finding the source of a leak could be cumbersome in the context of a real-world application. @@ -65,20 +54,23 @@ server.listen(9999, (err, address) => { ## Running with garbage collection traces You can see traces for garbage collection in the standard output of your process using the `--trace_gc` flag. -``` -node --trace_gc server.js +```bash +node --trace_gc script.mjs ``` +> Note: you can find the source code of this exercice in the [Node.js Diagnostics repository](https://github.com/nodejs/diagnostics/tree/main/documentation/memory/step3/exerice). + It should output something like: ``` bash -[13973:0x110008000] 44 ms: Scavenge 2.4 (3.2) -> 2.0 (4.2) MB, 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure -[13973:0x110008000] 75 ms: Scavenge 2.7 (4.7) -> 2.4 (5.4) MB, 0.9 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure -[13973:0x110008000] 151 ms: Scavenge 3.9 (5.7) -> 3.4 (8.5) MB, 0.6 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure -[13973:0x110008000] 181 ms: Scavenge 5.3 (8.5) -> 4.3 (8.7) MB, 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure -[13973:0x110008000] 245 ms: Scavenge 6.8 (9.8) -> 5.9 (10.5) MB, 0.6 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure -[13973:0x110008000] 271 ms: Scavenge 7.5 (10.5) -> 6.4 (15.8) MB, 0.9 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure -server listening on http://127.0.0.1:9999 +[39067:0x158008000] 2297 ms: Scavenge 117.5 (135.8) -> 102.2 (135.8) MB, 0.8 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure +[39067:0x158008000] 2375 ms: Scavenge 120.0 (138.3) -> 104.7 (138.3) MB, 0.9 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure +[39067:0x158008000] 2453 ms: Scavenge 122.4 (140.8) -> 107.1 (140.8) MB, 0.7 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure +[39067:0x158008000] 2531 ms: Scavenge 124.9 (143.3) -> 109.6 (143.3) MB, 0.7 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure +[39067:0x158008000] 2610 ms: Scavenge 127.1 (145.5) -> 111.8 (145.5) MB, 0.7 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure +[39067:0x158008000] 2688 ms: Scavenge 129.6 (148.0) -> 114.2 (148.0) MB, 0.8 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure +[39067:0x158008000] 2766 ms: Scavenge 132.0 (150.5) -> 116.7 (150.5) MB, 1.1 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure +Total: 1000000 entries ``` Hard to read? Maybe we should pass in review a few concepts and explain the outputs of the `--trace-gc` flag. @@ -156,42 +148,57 @@ This algorithm is composed of two phases: ### Memory leak -Now we can return to the output of the `--trace-gc` flag and see how we could interpret the result. - -* First, install (`autocannon`)[https://www.npmjs.com/package/autocannon]: -```bash -npm i -g autocannon -``` - -* Then, restart the server: -```bash -node --trace-gc server.js -``` - -* Open a new terminal and run the following command: -```bash -autocannon http://localhost:9999/write -``` - Now, if you return quickly to the previous terminal window: you will see many `Mark-sweep` events in the console. We also see that the amount of memory collected after the event is insignificant. Now that we are experts in garbage collection! What could we deduce? -We probably have a memory leak! But how could we point to the correct object in the code? (Reminder: it is pretty apparent in this example, but what about a real-world application?) +We probably have a memory leak! But how could we be sure of that? (Reminder: it is pretty apparent in this example, but what about a real-world application?) But how could we spot the context? ### How to get the context of bad allocations -We previously observed that the old space is continuously increasing. The following steps will help you to know which part of the code is responsible. - - 1. Review the trace data and figure out how much is the total heap before and after the GC. - 2. Use the [`--max-old-space-size`](https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-megabytes) to reduce the total old space size. 3. Run the program until you hit the out of memory. 4. The produced log shows the failing context. 6. If it hits OOM, increment the heap size by ~10% and repeat a few times. If the same pattern is observed, it indicates a memory leak. 7. If there is no OOM, then freeze the heap size to that value - A packed heap reduces memory footprint and computation latency. +For example, try to run `script.js` with the following command: + +```bash +node --trace-gc --max-old-space-size=50 script.mjs +``` + +You should experience an OOM: + +```bash +[...] +<--- Last few GCs ---> + +[40928:0x148008000] 509 ms: Mark-sweep 46.8 (65.8) -> 40.6 (77.3) MB, 6.4 / 0.0 ms (+ 1.4 ms in 11 steps since start of marking, biggest step 0.2 ms, walltime since start of marking 24 ms) (average mu = 0.977, current mu = 0.977) finalize incrementa[40928:0x148008000] 768 ms: Mark-sweep 56.3 (77.3) -> 47.1 (83.0) MB, 35.9 / 0.0 ms (average mu = 0.927, current mu = 0.861) allocation failure scavenge might not succeed + + +<--- JS stacktrace ---> + +FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory [...] +``` + +Now, try to it for 100mb: + +```bash +node --trace-gc --max-old-space-size=100 script.mjs +``` + +You should exeprience something similar, the only difference will be the last GC trace that should be last GC trace that contains a bigger heap size. + +```bash +<--- Last few GCs ---> + +[40977:0x128008000] 2066 ms: Mark-sweep (reduce) 99.6 (102.5) -> 99.6 (102.5) MB, 46.7 / 0.0 ms (+ 0.0 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 47 ms) (average mu = 0.154, current mu = 0.155) allocati[40977:0x128008000] 2123 ms: Mark-sweep (reduce) 99.6 (102.5) -> 99.6 (102.5) MB, 47.7 / 0.0 ms (+ 0.0 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 48 ms) (average mu = 0.165, current mu = 0.175) allocati +``` + +> Note: In the context of real application, it could be cumbersome to find the leaked object in the code. Heap snpashot could help you to find it. Visit the [guide dedicated to heap snapshot](https://github.com/nodejs/nodejs.org/blob/main/locale/en/docs/guides/diagnostics/memory/using-heap-snapshot.md#how-to-find-a-memory-leak-with-heap-snapshots). + ### Slowness How do you assert whether too many garbage collections are happening or causing an overhead? @@ -202,6 +209,68 @@ How do you assert whether too many garbage collections are happening or causing 4. If the time between two GCS and the time spent in GC are very high, probably the application can use a smaller heap. 5. If the time between two GCS is much greater than the time spent in GC, the application is relatively healthy. +## Fix the leak + +Now let's fix the leak. Instead of using a object to store our entries we could use a file. + +Let's modify our script a bit: + +```js +import os from 'os'; +import fs from 'fs/promises'; + +let len = 1_000_000; +const fileName = `entries-${Date.now()}`; + +async function addEntry () { + const entry = { + timestamp: Date.now(), + memory: os.freemem(), + totalMemory: os.totalmem(), + uptime: os.uptime(), + }; + + await fs.appendFile(fileName, JSON.stringify(entry) + '\n'); +} + +async function summary () { + const stats = await fs.lstat(fileName); + console.log(`File size ${stats.size} bytes`); +} + +// execution +(async () => { + await fs.writeFile(fileName, "----START---\n"); + while (len > 0) { + await addEntry(); + process.stdout.write(`~~> ${len} entries to record\r`); + len--; + }; + + await summary(); +})(); +``` + +Using a `Set` to store data is not a bad practice at all, you should just care about the memory footprint of your program. + +> Note: you can find the source code of this exercice in the [Node.js Diagnostics repository](https://github.com/nodejs/diagnostics/tree/main/documentation/memory/step3/exerice). + +Now, let's execute this script. + +``` +node --trace-gc script-fix.mjs +``` + +You should observe two things: +* the Mark-sweep events appears less frequently +* the memory footprint doesn't exceed 25mb versus more than 130mb with the first script. + +It makes a lot of sense as the new version put less pressure on the memory than the first one. + +**Takeway**: What do you think about improving this script? You probably see that the new version of the script is slow. What if we use a `Set` again and write its content into a file only when the memory reach a certain size? + +> (`getheapstatistics`)[https://nodejs.org/dist/latest-v16.x/docs/api/v8.html#v8getheapstatistics] API could help you. + ## Bonus: Trace garbage collection programmatically ### Using `v8` module @@ -209,19 +278,14 @@ How do you assert whether too many garbage collections are happening or causing You might want to avoid getting traces from the entire lifetime of your process running on a server. In that case, set the flag from within the process. The `v8` module exposes an API to put flags on the fly. ```js -/// at the top of the 'server.js' file, please add: import v8 from 'v8'; -// then on the body, add: +// enabling trace_gc +v8.setFlagsFromString('--trace_gc'); -server.get('/enable-gc-traces', () => { - v8.setFlagsFromString('--trace_gc'); -}); - -server.get('/disable-gc-traces', () => { - v8.setFlagsFromString('--notrace_gc'); -}); -``` +// disabling trace_gc +v8.setFlagsFromString('--notrace_gc'); + ``` ### Using performance hooks From ddb718948a97798627b258116fe12b649024b530 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Tue, 16 Aug 2022 17:37:26 +0200 Subject: [PATCH 08/10] doc: fix typos --- documentation/memory/step3/using_gc_traces.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/memory/step3/using_gc_traces.md b/documentation/memory/step3/using_gc_traces.md index 7d447e3..b42396c 100644 --- a/documentation/memory/step3/using_gc_traces.md +++ b/documentation/memory/step3/using_gc_traces.md @@ -189,7 +189,7 @@ Now, try to it for 100mb: node --trace-gc --max-old-space-size=100 script.mjs ``` -You should exeprience something similar, the only difference will be the last GC trace that should be last GC trace that contains a bigger heap size. +You should experience something similar, the only difference should be that the last GC trace will contain a bigger heap size. ```bash <--- Last few GCs ---> @@ -211,7 +211,7 @@ How do you assert whether too many garbage collections are happening or causing ## Fix the leak -Now let's fix the leak. Instead of using a object to store our entries we could use a file. +Now let's fix the leak. Instead of using an object to store our entries, we could use a file. Let's modify our script a bit: @@ -251,7 +251,7 @@ async function summary () { })(); ``` -Using a `Set` to store data is not a bad practice at all, you should just care about the memory footprint of your program. +Using a `Set` to store data is not a bad practice at all; you should just care about the memory footprint of your program. > Note: you can find the source code of this exercice in the [Node.js Diagnostics repository](https://github.com/nodejs/diagnostics/tree/main/documentation/memory/step3/exerice). @@ -262,12 +262,12 @@ node --trace-gc script-fix.mjs ``` You should observe two things: -* the Mark-sweep events appears less frequently -* the memory footprint doesn't exceed 25mb versus more than 130mb with the first script. +* Mark-sweep events appear less frequently +* the memory footprint doesn't exceed 25MB versus more than 130MB with the first script. -It makes a lot of sense as the new version put less pressure on the memory than the first one. +It makes a lot of sense as the new version puts less pressure on the memory than the first one. -**Takeway**: What do you think about improving this script? You probably see that the new version of the script is slow. What if we use a `Set` again and write its content into a file only when the memory reach a certain size? +**Takeaway**: What do you think about improving this script? You probably see that the new version of the script is slow. What if we use a `Set` again and write its content into a file only when the memory reaches a specific size? > (`getheapstatistics`)[https://nodejs.org/dist/latest-v16.x/docs/api/v8.html#v8getheapstatistics] API could help you. From f09c2d0ab926bb6c80747b87b9e928cd9e1af34a Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Wed, 17 Aug 2022 18:21:55 +0200 Subject: [PATCH 09/10] chore: clean up --- .../{exercice => exercise}/script-fix.mjs | 0 .../step3/{exercice => exercise}/script.mjs | 0 documentation/memory/step3/using_gc_traces.md | 345 ------------------ 3 files changed, 345 deletions(-) rename documentation/memory/step3/{exercice => exercise}/script-fix.mjs (100%) rename documentation/memory/step3/{exercice => exercise}/script.mjs (100%) delete mode 100644 documentation/memory/step3/using_gc_traces.md diff --git a/documentation/memory/step3/exercice/script-fix.mjs b/documentation/memory/step3/exercise/script-fix.mjs similarity index 100% rename from documentation/memory/step3/exercice/script-fix.mjs rename to documentation/memory/step3/exercise/script-fix.mjs diff --git a/documentation/memory/step3/exercice/script.mjs b/documentation/memory/step3/exercise/script.mjs similarity index 100% rename from documentation/memory/step3/exercice/script.mjs rename to documentation/memory/step3/exercise/script.mjs diff --git a/documentation/memory/step3/using_gc_traces.md b/documentation/memory/step3/using_gc_traces.md deleted file mode 100644 index b42396c..0000000 --- a/documentation/memory/step3/using_gc_traces.md +++ /dev/null @@ -1,345 +0,0 @@ -# Tracing garbage collection - -This guide will go through the fundamentals of garbage collection traces. - -By the end of this guide, you'll be able to: -* Enable traces in your Node.js application -* Interpret traces -* Identify potential memory issues in your Node.js application - -There's probably a lot of stuff to learn about how the garbage collector works, but if you have to know one thing, it's that when GC is running, your code is not. - -You may want to know how often and long the garbage collection runs. - -## Setup - -For the proposal of this guide, we'll use this script: - -```js -// script.js -import os from 'os'; - -let len = 1_000_000; -const entries = new Set(); - -function addEntry () { - const entry = { - timestamp: Date.now(), - memory: os.freemem(), - totalMemory: os.totalmem(), - uptime: os.uptime(), - }; - - entries.add(entry); -} - -function summary () { - console.log(`Total: ${entries.size} entries`); -} - -// execution -(() => { - while (len > 0) { - addEntry(); - process.stdout.write(`~~> ${len} entries to record\r`); - len--; - }; - - summary(); -})(); -``` - -> Even if the leak is evident here, finding the source of a leak could be cumbersome in the context of a real-world application. - -## Running with garbage collection traces -You can see traces for garbage collection in the standard output of your process using the `--trace_gc` flag. - -```bash -node --trace_gc script.mjs -``` - -> Note: you can find the source code of this exercice in the [Node.js Diagnostics repository](https://github.com/nodejs/diagnostics/tree/main/documentation/memory/step3/exerice). - -It should output something like: - -``` bash -[39067:0x158008000] 2297 ms: Scavenge 117.5 (135.8) -> 102.2 (135.8) MB, 0.8 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure -[39067:0x158008000] 2375 ms: Scavenge 120.0 (138.3) -> 104.7 (138.3) MB, 0.9 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure -[39067:0x158008000] 2453 ms: Scavenge 122.4 (140.8) -> 107.1 (140.8) MB, 0.7 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure -[39067:0x158008000] 2531 ms: Scavenge 124.9 (143.3) -> 109.6 (143.3) MB, 0.7 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure -[39067:0x158008000] 2610 ms: Scavenge 127.1 (145.5) -> 111.8 (145.5) MB, 0.7 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure -[39067:0x158008000] 2688 ms: Scavenge 129.6 (148.0) -> 114.2 (148.0) MB, 0.8 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure -[39067:0x158008000] 2766 ms: Scavenge 132.0 (150.5) -> 116.7 (150.5) MB, 1.1 / 0.0 ms (average mu = 0.994, current mu = 0.994) allocation failure -Total: 1000000 entries -``` - -Hard to read? Maybe we should pass in review a few concepts and explain the outputs of the `--trace-gc` flag. - -### Examining a trace with `--trace_gc` - -The `--trace-gc` flag outputs all garbage collection events in the console. The composition of each line can be described as: - -```bash -[13973:0x110008000] 44 ms: Scavenge 2.4 (3.2) -> 2.0 (4.2) MB, 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) allocation failure -``` - -| Token value | Interpretation | -|-------------------------------------------------------------|------------------------------------------| -| 13973 | PID of the running process | -| 0x110008000 | Isolate (JS heap instance) | -| 44 ms | The time since the process started in ms | -| Scavenge | Type / Phase of GC | -| 2.4 | Heap used before GC in MB | -| (3.2) | Total heap before GC in MB | -| 2.0 | Heap used after GC in MB | -| (4.2) | Total heap after GC in MB | -| 0.5 / 0.0 ms (average mu = 1.000, current mu = 1.000) | Time spent in GC in ms | -| allocation failure | Reason for GC | - -We'll only focus on two events here: -* Scavenge -* Mark-sweep - -The heap is divided into _spaces_. Amongst these, we have a space called the "new" space and another one called the "old" space. - -> 👉 In reality, the heap structure is a bit different, but we'll stick to a simpler version for this article. If you want more details, I encourage you to look at this [talk of Peter Marshall](https://v8.dev/blog/trash-talk) about Orinoco. - -### Scavenge - -Scavenge is the name of an algorithm that will perform garbage collection into new space. -The new space is where objects are created. The new space is designed to be small and fast for garbage collection. - -Let's imagine a Scavenge scenario: - -* we allocated `A`, `B`, `C` & `D`. - ```bash - | A | B | C | D | | - ``` -* we want to allocate `E` -* not enough space, the memory is exhausted -* then, a (garbage) collection is triggered -* dead objects are collected -* living object will stay -* assuming `B` and `D` were dead - ```bash - | A | C | | - ``` -* now we can allocate `E` - ```bash - | A | C | E | | - ``` - -v8 will promote objects, not garbage collected after two Scavenge operations to the old space. - -> 👉 Full [Scavenge scenario](https://github.com/thlorenz/v8-perf/blob/master/gc.md#sample-scavenge-scenario). - -### Mark-sweep - -Mark-sweep is used to collect objects from old space. The old space is where objects that survived the new space are living. - -This algorithm is composed of two phases: -* **Mark**: Will mark still alive objects as black and others as white. -* **Sweep**: Scans for white objects and converts them to free spaces. - -> 👉 In fact, the Mark and Sweep steps are a bit more elaborate. Please read this [document](https://github.com/thlorenz/v8-perf/blob/master/gc.md#marking-state) for more details. - - -## `--trace-gc` in action - -### Memory leak - -Now, if you return quickly to the previous terminal window: you will see many `Mark-sweep` events in the console. We also see that the amount of memory collected after the event is insignificant. - -Now that we are experts in garbage collection! What could we deduce? - -We probably have a memory leak! But how could we be sure of that? (Reminder: it is pretty apparent in this example, but what about a real-world application?) - -But how could we spot the context? - -### How to get the context of bad allocations - - 3. Run the program until you hit the out of memory. - 4. The produced log shows the failing context. - 6. If it hits OOM, increment the heap size by ~10% and repeat a few times. If the same pattern is observed, it indicates a memory leak. - 7. If there is no OOM, then freeze the heap size to that value - A packed heap reduces memory footprint and computation latency. - -For example, try to run `script.js` with the following command: - -```bash -node --trace-gc --max-old-space-size=50 script.mjs -``` - -You should experience an OOM: - -```bash -[...] -<--- Last few GCs ---> - -[40928:0x148008000] 509 ms: Mark-sweep 46.8 (65.8) -> 40.6 (77.3) MB, 6.4 / 0.0 ms (+ 1.4 ms in 11 steps since start of marking, biggest step 0.2 ms, walltime since start of marking 24 ms) (average mu = 0.977, current mu = 0.977) finalize incrementa[40928:0x148008000] 768 ms: Mark-sweep 56.3 (77.3) -> 47.1 (83.0) MB, 35.9 / 0.0 ms (average mu = 0.927, current mu = 0.861) allocation failure scavenge might not succeed - - -<--- JS stacktrace ---> - -FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory [...] -``` - -Now, try to it for 100mb: - -```bash -node --trace-gc --max-old-space-size=100 script.mjs -``` - -You should experience something similar, the only difference should be that the last GC trace will contain a bigger heap size. - -```bash -<--- Last few GCs ---> - -[40977:0x128008000] 2066 ms: Mark-sweep (reduce) 99.6 (102.5) -> 99.6 (102.5) MB, 46.7 / 0.0 ms (+ 0.0 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 47 ms) (average mu = 0.154, current mu = 0.155) allocati[40977:0x128008000] 2123 ms: Mark-sweep (reduce) 99.6 (102.5) -> 99.6 (102.5) MB, 47.7 / 0.0 ms (+ 0.0 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 48 ms) (average mu = 0.165, current mu = 0.175) allocati -``` - -> Note: In the context of real application, it could be cumbersome to find the leaked object in the code. Heap snpashot could help you to find it. Visit the [guide dedicated to heap snapshot](https://github.com/nodejs/nodejs.org/blob/main/locale/en/docs/guides/diagnostics/memory/using-heap-snapshot.md#how-to-find-a-memory-leak-with-heap-snapshots). - -### Slowness - -How do you assert whether too many garbage collections are happening or causing an overhead? - - 1. Review the trace data, precisely the time between consecutive collections. - 2. Review the trace data, specifically around time spent in GC. - 3. If the time between two GC is less than the time spent in GC, the application is severely starving. - 4. If the time between two GCS and the time spent in GC are very high, probably the application can use a smaller heap. - 5. If the time between two GCS is much greater than the time spent in GC, the application is relatively healthy. - -## Fix the leak - -Now let's fix the leak. Instead of using an object to store our entries, we could use a file. - -Let's modify our script a bit: - -```js -import os from 'os'; -import fs from 'fs/promises'; - -let len = 1_000_000; -const fileName = `entries-${Date.now()}`; - -async function addEntry () { - const entry = { - timestamp: Date.now(), - memory: os.freemem(), - totalMemory: os.totalmem(), - uptime: os.uptime(), - }; - - await fs.appendFile(fileName, JSON.stringify(entry) + '\n'); -} - -async function summary () { - const stats = await fs.lstat(fileName); - console.log(`File size ${stats.size} bytes`); -} - -// execution -(async () => { - await fs.writeFile(fileName, "----START---\n"); - while (len > 0) { - await addEntry(); - process.stdout.write(`~~> ${len} entries to record\r`); - len--; - }; - - await summary(); -})(); -``` - -Using a `Set` to store data is not a bad practice at all; you should just care about the memory footprint of your program. - -> Note: you can find the source code of this exercice in the [Node.js Diagnostics repository](https://github.com/nodejs/diagnostics/tree/main/documentation/memory/step3/exerice). - -Now, let's execute this script. - -``` -node --trace-gc script-fix.mjs -``` - -You should observe two things: -* Mark-sweep events appear less frequently -* the memory footprint doesn't exceed 25MB versus more than 130MB with the first script. - -It makes a lot of sense as the new version puts less pressure on the memory than the first one. - -**Takeaway**: What do you think about improving this script? You probably see that the new version of the script is slow. What if we use a `Set` again and write its content into a file only when the memory reaches a specific size? - -> (`getheapstatistics`)[https://nodejs.org/dist/latest-v16.x/docs/api/v8.html#v8getheapstatistics] API could help you. - -## Bonus: Trace garbage collection programmatically - -### Using `v8` module - -You might want to avoid getting traces from the entire lifetime of your process running on a server. In that case, set the flag from within the process. The `v8` module exposes an API to put flags on the fly. - -```js -import v8 from 'v8'; - -// enabling trace_gc -v8.setFlagsFromString('--trace_gc'); - -// disabling trace_gc -v8.setFlagsFromString('--notrace_gc'); - ``` - -### Using performance hooks - -For Node.js v8.5.0 or later, you can use [performance hooks](https://nodejs.org/api/perf_hooks.html) to trace garbage collection. - -```js -const { PerformanceObserver } = require('perf_hooks'); - -// Create a performance observer -const obs = new PerformanceObserver((list) => { - const entry = list.getEntries()[0] - /* - The entry would be an instance of PerformanceEntry containing - metrics of garbage collection. - For example: - PerformanceEntry { - name: 'gc', - entryType: 'gc', - name: 'gc', - duration: 1.315709, - kind: 1 - } - */ -}); - -// Subscribe notifications of GCs -obs.observe({ entryTypes: ['gc'] }); - -// Stop subscription -obs.disconnect(); -``` - -You can get GC statistics as [PerformanceEntry](https://nodejs.org/api/perf_hooks.html#perf_hooks_class_performanceentry) from the callback in [PerformanceObserver](https://nodejs.org/api/perf_hooks.html#perf_hooks_class_performanceobserver). - -For example: - -```js -PerformanceEntry { - name: 'gc', - entryType: 'gc', - startTime: 2820.567669, - duration: 1.315709, - kind: 1 -} -``` - -| Property | Interpretation | -|------------|-------------------------------------------------------------------------------------------------| -| name | The name of the performance entry. | -| entryType | The type of the performance entry. | -| startTime | The high-resolution millisecond timestamp is marking the starting time of the Performance Entry.| -| duration | The total number of milliseconds elapsed for this entry. | -| kind | The type of garbage collection operation that occurred. | -| flags | The high-resolution millisecond timestamp is marking the starting time of the Performance Entry.| - -For more information, refer to [the documentation about performance hooks](https://nodejs.org/api/perf_hooks.html). - From 7a1edc6f4dd3f5ffade0c2dce7e6746931853ce9 Mon Sep 17 00:00:00 2001 From: Tony Gorez Date: Wed, 17 Aug 2022 23:15:01 +0200 Subject: [PATCH 10/10] chore: add readme --- documentation/memory/step3/exercise/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 documentation/memory/step3/exercise/README.md diff --git a/documentation/memory/step3/exercise/README.md b/documentation/memory/step3/exercise/README.md new file mode 100644 index 0000000..3bccadf --- /dev/null +++ b/documentation/memory/step3/exercise/README.md @@ -0,0 +1,4 @@ +# Tracing garbage collection exercices + +These scripts are materials for the [GC traces](https://nodejs.org/en/docs/guides/diagnostics/memory/using-gc-traces/) tutorial. +