-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extend constructor to accept resource constraints #41
base: master
Are you sure you want to change the base?
Extend constructor to accept resource constraints #41
Conversation
This is great, thanks for the contribution! Can you document the new exported type & function, and add some basic lights-on tests? Better yet, add a test that verifies the memory constraints. |
Yes! I just got started and designing the unit test is probably the most interesting part, because if the memory threshold is hit, v8 just crashes with an OOM error message. You can set a
|
Interesting, can you expand on how this new feature works? I was under the impression that if you set the resource constraint and it's exceeded within the JS VM, then any JS execution will fail with some v8 error. What do you mean by "v8 just crashes with an OOM error message" -- do you mean that it crashes the process? or returns an error to go with the OOM? If the latter, then a test that just looks for that error should be sufficient. |
It actually brings down the whole process. The moment the heap is running out of memory, v8 will kill the process with a fatal error. I think v8 starts by default with I've prototyped something where you can define your own OOM Error Handler in Go. I'll update my PR shortly. |
6ed3035
to
1173852
Compare
The v8 API makes it possible to set your own OOM error handler. This is useful for us, because what we want is to let v8 run into a controlled OOM scenario and then let the test pass. Since this is a Go wrapper we should be able to define this handler in Go and not only C and make it configurable to the client using it. I've done that by exporting the function I'd rather make it possible to configure the OOM error handler per Isolate, but my cgo knowledge is not sufficient to know how to make this work. When I try to export a Go method as part of a struct it won't work. It looks like I can only export functions but no methods to cgo. So I have no idea how to scope this per isolate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on adding the test. Let me know when it's ready for review.
v8.go
Outdated
|
||
// NewIsolateWithConstraints creates a new V8 Isolate applying additional | ||
// resource constraints to limit the V8 runtime. | ||
func NewIsolateWithConstraints(constraints ResourceConstraints) *Isolate { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In retrospect, the NewIsolateWithSnapshot
was a mistake -- we should have NewIsolateWithOptions
so that every new constructor option doesn't add another constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Is it too late for that given that the existing API shouldn't be broken?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's too late to remove NewIsolateWithSnapshot
, but we can add NewIsolateWithOptions
and stem the bleeding. Add a note that NewIsolateWithSnapshot
is deprecated and just forward the implementation to a call to ...WithOptions
|
||
type ResourceConstraints struct { | ||
// MaxOldSpaceSize sets the maximum size of the old object heap in MiB. | ||
MaxOldSpaceSize int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please doc the behavior when the resource limits are hit. That it crashed the process was a surprise! I'm happy that you're adding the OOM handler.
v8_test.go
Outdated
isolate := NewIsolateWithConstraints(ResourceConstraints{MaxOldSpaceSize: 10}) | ||
isolate.SetOOMErrorHandler(func(location string, isHeapOOM bool) { | ||
// Success | ||
os.Exit(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not ok to do in a test since it'll interrupt other tests. If any other tests are failing, you might not see that because they are run in parallel but this one exits the entire test process before anything else fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad you mentioned this. I assumed the same but didn't see the other tests stopping but didn't think of the implications of parallel test execution!
I am wondering if there's another way. Simply, without os.Exit(0)
the test will fail because of the v8 runtime sending a non-0 exit code.
There's also a SetFatalErrorHandler
we could set for v8, maye that would allow to override the behavior for the test scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need to test that the process crashes, then you can have this test run a separate test process:
https://stackoverflow.com/questions/26225513/how-to-test-os-exit-scenarios-in-go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(though I'd prefer that we end up with a cleaner situation than crashing the process -- if you end up with a crash-the-process solution, please be VERY VERY clear in the docs about the side-effects!)
v8_test.go
Outdated
@@ -1541,3 +1542,23 @@ func TestPanicHandling(t *testing.T) { | |||
_ = NewIsolate() | |||
_ = *f | |||
} | |||
|
|||
func TestNewIsolateWithConstraints(t *testing.T) { | |||
// Creates a v8 runtime where the memory is limited to 10MB and memory is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to set it as high as 10MB -- 1MB is fine. Tests should be as lightweight as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With 1MB it seems NewIsolate()
doesn't even terminate. I think there's a minimum required memory to get v8 started. This should probably be also tested as part of the configuration input, i.e. illegal parameters.
This is indeed the case. We fight with the same thing when handling the callbacks: I think you should be able to piggyback on that code (or, if not, use the same approach). I'd like to help but I won't have time in the next few weeks to allocate to this, sorry. |
Awesome! No worries, a blueprint is all I need. I'll get to it :) |
Yeah, I think that should work.
…On Tue, May 7, 2019 at 8:19 AM Konrad Reiche ***@***.***> wrote:
I just read through the code of callback magic. Since this implementation
is scoped to the v8.Context object and adding OOM error handlers is
scoped to v8.Isolate should I go ahead and create another registry, i.e.:
var contexts = map[int]*refCount{}
var contextsMutex sync.RWMutex
var nextContextId int
var isolates = map[int]*refCount{}
var isolatesMutex sync.RWMutex
var nextIsolateId int
?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#41 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAFFFVM6VKWPOSBMUUM7ZI3PUGFYPANCNFSM4GUODUAA>
.
|
1173852
to
d7ada8c
Compare
Coming back to this after picking my brain for a while on this topics. Here are my thoughts: I assumed setting an OOM error handler per isolate would make sense since this is what the v8 API exposes. After implementing everything I realized when an OOM error happens, there is no way to recover from it:
So it still stands, that we want to make that happen in Go code. So I came back to my initial implementation, set an OOM error handler globally with I've also changed the code to have a To reiterate on some of your thoughts @augustoroman. My code changes don't introduce any new situations where the process can come down crashing. With the code currently on master, the same thing can happen when you create a new isolate and keep on allocating memory. When you allocate more than 512MB of memory, v8 will crash, and thus the Go process. Obviously, it's a lot harder to get OOM with 512MB than with 10MB so extending the documentation to point out the importance is still something I need to do. What do you think of this so far? I've also wrote to the v8-dev mailing list for clarification on the OOM error handling and why it's per isolate so we don't make any mistakes here https://groups.google.com/forum/#!topic/v8-dev/fvw21ElZlSY |
For a project we're in dire need to limit the runtime's memory used by the VM. For that I've extended the Isolate constructor to accept the resource constraint type.