So as we can see, DevTools and the memory heap snapshot mechanism is a great way to find and fix memory leaks and this way resolve the CPU and memory issues. But is it the only way? But first of all, let's talk about the benefits and limitations of this approach. The benefits is that it's very easy to use in the development cycle. There are multiple development tools that I can use. I can use it with the Chrome DevTools. I can use it with the DevTools that are built in, for example, to VS Code. I can also profile a leak during startup using Inspect BRK instead of Inspect. But there are also downsides to this approach. It requires a restart in order to add that CLI flag that puts the node in the development mode. It requires access from the development environment to the node instance using the Chrome debugging protocol, which is a security issue even though you can tunnel it over SSH. There is obviously performance overhead for running the node instance in debug mode, and I can also accidentally break the service by just pressing the pause button in the development environment. Also, taking snapshots is really heavy, and if I'm not careful, I can actually kill the process just by taking a memory snapshot.
Another cool way of getting a snapshot is to use a new experimental node flag, heap snapshot near heap limit, and you specify how many heap snapshots to take. So what happens is that when node nears the heap memory limit, it will take a snapshot up to three times in this case, so that I can still do the diff and find the reason what is actually leaking by doing the comparison between the snapshots. This is a cool way, but you need to be careful with it because, again, taking that snapshot might just drive you over the limit and cause the process to crash. I actually prefer another way, which is to specify that node will take a snapshot automatically when it receives a signal. So in this case, I use the node snapshot signal equals siguser2, and then if I use the linux kill command to send that signal to that process, I will actually cause it to generate a heap dump file in a specified folder. What are the benefits of this approach? First of all, it's set and forget. You can put it in production as long as you're not taking snapshots. There is no overhead, and it's secure because I don't need to open a debug port to that instance. On the downside, I do need to be able to access that Linux box, for example, using SSH in order to send the signal to that process. Also, the dumps themselves take up space on the server, and you need to be careful about how many dumps you actually put there before cleaning up to make sure that you don't blow up your disk. And, again, taking the snapshot, it's still a heavy operation and can push you over the limit.
The third way is actually using an API. There are built-in APIs in Node that you can use to generate heap dumps. So you can implement, for example, your own TCP endpoint that listens for some sort of instruction and generates a heap dump on demand and puts it wherever you want it to go. Obviously, that takes a little bit more effort. The benefits of using an API, you can control exactly when and how to invoke it. And, again, there is no overhead when idle. There is only overhead when you actually invoke that endpoint.
Comments