RuntimeError with WebAssembly in @react-pdf/yoga on Node.js Server #1572
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Hello,
I've encountered a critical issue while using @react-pdf/renderer in a Node.js Express server environment. The server crashes with a RuntimeError related to WebAssembly memory allocation when attempting to generate PDFs. The error message is as follows:
This error suggests that the WebAssembly module within yoga-layout, which is a dependency of @react-pdf/renderer, is running out of memory.
To Reproduce
Expected Behavior:
The PDF generation should complete successfully without causing a server crash or running out of WebAssembly memory.
Actual Behavior:
The server crashes with a RuntimeError indicating that WebAssembly ran out of memory.
Additional Context:
The issue seems to occur after generating multiple PDFs, suggesting a potential memory leak or insufficient memory allocation for WebAssembly in the yoga-layout module.
Reducing the complexity of the PDF layout temporarily mitigates the issue but does not solve it entirely.
I would appreciate any insights or suggestions on how to resolve this issue. If there are any known workarounds or if additional information is needed, please let me know.
Environment:
Node.js version: v16.18.1
Express version: ^4.18.2
@react-pdf/layout": "3.10.3",
@react-pdf/renderer": "3.3.4",
Operating System: docker image
Thank you for your time and assistance.
This is going to be hard to diagnose without more information about the usage.
The Yoga wasm binary allows heap growth (
58aa090774/javascript/CMakeLists.txt (L37)
) and provides methods to free nodes/configs.Without knowing more, out of memory seems like the consuming library might not be freeing resources, or that there is an irregularly large tree.
Not sure if this is relevant, but in my (browser based) benchmarking scripts where I was constructing, measuring and then destructing Yoga trees in a loop, I was hitting a similar crash. It took quite high iteration count (>10000) to hit it, but it was with a script that in theory shouldn't have increased heap size at all. This also occurred with a similar Taffy-based benchmark at similar iteration counts.
It could perhaps be due to heap fragmentation? Or immaturity of WASM implementations? Although I must admit that I haven't investigated this thoroughly enough to rule out user error.
As a workaround, if this is only occurring after repeated PDF generation, then you could try the following steps (in order):
I took a quick look at this, and other issues running into it: https://github.com/vercel/next.js/issues/51870
I missed that this error happens during WASM environment instantiation. So, this error indicates that the WASM is being loaded into a new environment, and Node doesn't have enough memory to do that.
I am wondering if this scenario is somehow doing this loading more than once, or if maybe multiple processes are doing this, and running out of memory.
I went to double check if we have documented guarantees from Emscripten module that it is only loaded once on load call, but couldn't quickly verify. We are pushing out Yoga 3.0 now, which will cache the instance regardless.
It seem that for node.js yoga-layout leaks memory after 3.1 because downgrading to 3.0.4 does not accumulate memory.
https://github.com/diegomura/react-pdf/issues/3051#issuecomment-2654825847
heapdump points to minifyed
yoga-wasm-base64-esm.js
but tbh I hit the wall there with my debugging skills 😅That's really fascinating. There was not much in the way of significant changes to JS layer between these versions.
bbdd1afe59
is the most interesting thing I can find, where we added a new entrypoint.If we are repeatedly instantiating wasm, I wonder if somehow environment is losing track of cached loaded module or something. Could be interesting to try to revert locally, or bisect (
yarn prepack
should be enough to create local npm package of any commit).Hello!
I don't know if it's the same issue, but I'll put it here in case it helps.
I've got a quite complicated setup (react-three/uikit, custom layouting for text) and there's definitely something wrong happening when updating measure function via node.setMeasureFunc. I'm not very familiar with WASM debugging, but as far as I could tell, something wrong with calling the destructor for the old measure function. I get either this error, or something like "null function or invalid function signature" error. I don't know yet how to reproduce it outside of my setup, but it happens not every time and the error sometimes changes, so I think there's something going wrong with the heap.
Sorry for erratic wording, I hope this can help. I think I will continue to investigate it on my side. Hope this helps a bit.
UPD:
Sometimes the error is "memory access out of bounds"
I've managed to reproduce the bug, please see #1818
Hello, we are facing this error and quite a significant memory leak when using with
@react-pdf/renderer
.We use version 3.2.1. Rolled back to 3.0.4 and there was memory leak.
I was bisected the commits between 3.0.4 and 3.1.0, the memory leak was introduced in this commit
Commenting out the
yoga::log
fixes the memory leak in 3.2.1dc2581f229/yoga/node/Node.cpp (L68-L73)
I am not too familiar with the codebase and C++. Perhaps there's something wrong with
log
in wasm that it captures references from the current context?I can reproduce the issue when using
@react-pdf/renderer
and boilerplate from this commitf3ccddf442
The reproduction is somewhat unclean as I added react and @react-pdf/renderer dependencies to the project to showcase the issue. However, it should help someone familiar with the codebase to figure out the root cause.
The commit currently has the log line commented out
Steps:
cd javascript
yarn
yarn memory-test
yarn memory-test
Having the log line significantly increases memory allocations that never free up by NodeJS Garbage Collector
Hi @NickGerleman, I created a new reproduction example of this issue that does not rely on
@react-pdf
to reduce investigation scope.https://github.com/Obi-Dann/yoga/pull/1
There's clearly something wrong with the way logging is setup in WASM/JS, even though
yoga::log
tries to output warnings they don't show up in the console. Perhaps that's the issue as they get captured somewhere?