`WARNING: v8::OwnedIsolate for snapshot was leaked` and/or crash after using snapshot
On startup, I create a runtime:
Then I will recreate the runtime, since snapshot consumes it:
But the next time I reload from the snapshot in this way I get
WARNING: v8::OwnedIsolate for snapshot was leaked
, and some of the extensions result in a crash:
My extremely roundabout method here seems off to me
So how do I actually use snapshot correctly to avoid this35 Replies
Ah yeah. In the second one,
JsRuntimeForSnapshot
should just be JsRuntime
We've played around with how we can make this API less terrible but it might be useful just to improve that warning message insteadIt's not the most specific message i have seen
The warning is gone but the crash is not gone
If you're using snapshotting, there's some very specific requirements around external references
Make sure you pass the extensions list to the second runtime
it's when I call this op:
Ah see there's the rub
Extension does not provide clone or copy
Yeah, you need to recreate it 🙂
and the structs they come from don't have a trait
GitHub
Create an
ExtensionSet
to handle logic around multiple extensions...There is a fair amount of logic in JsRuntime that deals with collating the data from multiple Extensions. We should move all of this logic to an ExtensionSet that can be initialized from a slice of...
The code in Deno to juggle snapshots + extensions + extending snapshots for workers is extremely complex
Maybe we could move the init_esm/init_ops stuff to a trait?
That would fix things for a lot of cases
allow copy on the structs, then call init from the trait
boom no clone needed
The long-term plan is that extensions would become const structs, but we're only about halfway through that work
Then extensions would be static consts and extension init becomes nearly free
in the meantime I guess the user will need to provide the extensions twice, in new() and in reset()
Yes
ouch
Yeah, I've struggled with it. If I had more bandwidth I'd fix it today
oddly the deno_console one still works after a reload with no extensions
No ops == no crash
No ops == no fun
The problem is in the bindings.rs stuff
If you want to PR a basic, #[doc(hidden)] extension trait I can definitely merge that
I've already started writing it haha
Sweet!
If you can make a very simple one with just the init_ functions for now, it'll make it easier to merge. It's probably better if we do this one small step at a time
hm right, the parameters in the macro
It would not be compatible with that option
What about something simple like adding to JsRuntime:
That way one could use that to extract loaded extensions just prior to snapshot,
Then provide them to the new Runtime during reload?
Because I don't see snapshot() reading from extensions
Unfortunately because we have the different extension modes this doesn't necessarily work
Then for the moment I think
Instead of having the user provide a vec of extensions,
I will take in a closure that returns the vec - so I can just call it twice!
That way no changes needed to deno_core
That would work 🙂
I fully agree that this interface is not great
It's just that solving it is not easy
(for example -- it is silly that you need to provide initialization data for snapshots)
Clone could work for Extensions if it weren;t for that meddling middleware
Yeah and snapshot consumes the runtime, so I need to do it twice
Yup
There should be a way to load a snapshot into a running instance without needing to create a new one
Acually - that might be a far easier change
Most of the time you create the snapshot and regular runtime in different processes, however
A different usecase than mine then for sure
Ok so... when I do provide the same set of Extensions during snapshot reload
I get a different crash, about the extensions being already loaded
Perhaps you've re-provided a module or extension that was already included in the snapshot
The one it is complaining about is the same one that crashes if I do not provide itI believe you need to use
init_ops
the second time and init_ops_and_esm
for the snapshot
Or maybe the other way around hmm....... so I can't use a closure to provide the extensions either then
Maybe a macro?
That duplicates the same calls for init_ops and init_ops_and_esm
New train of thought... can I unload a module? Make sure other modules can't import it, invalidate the id etc?
Because if so I'll just forget snapshots for now
They don't seem like a good fit for my use-case here
I'll just unload all the modules and reset globalThis
If not I smell a PR coming
Super easy:
In realms:
And in runtime:
I'll take a backup copy of the global object on startup,
Then I just have to revert that and call clear_modules() on my runtime instance
And boom, no snapshots
Yup, seems to let me reset, still use ops, and reload modules again!
If it sounds kosher to you I'll set up the PR
I think we will just need to make sure the v8 resources around the modules have been released. Are you able to see if memory is reclaimed by loading/unloading 1000 modules repeatedly?
Running the test now
Loading and unloading 1000 modules 1000 times
It's still going but its been over a minute and it keeps bouncing between 36 and 40k usage for the process 🙂 No steady climb
Test passed
I want to try again with a larger module, but seems stable so far
If that passes too I'll open a pr later tonight
PR is up