-
Notifications
You must be signed in to change notification settings - Fork 17
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
Experiment with no proxy for objects #1031
Conversation
"multiplicity": false, | ||
"nullable": true, | ||
"type": "string", | ||
}, |
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.
We were assuming this property in places but did not actually define it which caused me some problems early on.
[InterfaceDefRef]: interfaceDef, | ||
}; | ||
const interfaceHolder: InterfaceHolderOwnProps<Q> = Object.create(null, { | ||
[UnderlyingOsdkObject]: { value: underlying }, |
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.
I don't want these to be enumerable.
case "$primaryKey": | ||
case "$title": | ||
case "$objectType": | ||
case "$rid": | ||
return underlying[p] != null | ||
? Reflect.getOwnPropertyDescriptor(underlying, p) |
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.
Can't reflect in this version because the underlying is now frozen.
@@ -123,6 +131,8 @@ function createInterfaceProxyHandler( | |||
"$primaryKey", | |||
...(underlying["$rid"] ? ["$rid"] : []), | |||
"$title", | |||
UnderlyingOsdkObject, | |||
InterfaceDefRef, |
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 required now that they are no longer configurable/enumerable.
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.
Hmm, I'm still kinda confused why we add these here
{ | ||
{ | ||
{ | ||
{ |
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.
These just keep the diff simple. We can remove them before merge or in follow up
@@ -34,12 +33,10 @@ export interface ObjectHolderPrototypeOwnProps { | |||
readonly [ClientRef]: MinimalClient; | |||
readonly "$as": DollarAsFn; | |||
readonly "$link": ReturnType<typeof get$link>; | |||
readonly "$updateInternalValues": (newValues: Record<string, any>) => void; |
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.
Ok so now that we're just using the raw object, we don't need this
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.
Yup, this is why I said we have to give up on this functionality to achieve this.
@@ -123,6 +131,8 @@ function createInterfaceProxyHandler( | |||
"$primaryKey", | |||
...(underlying["$rid"] ? ["$rid"] : []), | |||
"$title", | |||
UnderlyingOsdkObject, | |||
InterfaceDefRef, |
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.
Hmm, I'm still kinda confused why we add these here
Object.defineProperties(rawObj, { | ||
[ObjectDefRef]: { value: objectDef, enumerable: false }, | ||
[ClientRef]: { value: client, enumerable: false }, | ||
[RawObject]: { |
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.
What's the difference between RawObject
and UnderlyingOsdkObject
? I think I'm getting tripped up because I know type wise the RawObject
is what we get over the wire and the underlying is like OsdkBase
, but here we set both values to rawObj
? Probably missing something here
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.
Nothing. But Im not trying to change ALL the code in the system to demonstrate JUST these objects being raw instead of proxies. The existing contract required both of these things, they just happen to be the same now.
If we go forward, this wouldn't be kept like this, we would clean it all up. But redoing the contract for interfaces would just muddy up this change (and I might as well just remove all proxies at that point since it would be far reaching).
The goal here was to show that the objects could not be proxies and what that would be like (roughly) so we could discuss taking the next steps.
9a7b268
to
d496d03
Compare
reorder for diff reorder for diff reorder for diff reorder for diff update snapshot
ff79245
to
8b022a2
Compare
BLUF
This PR removes the proxy objects in favor of simply adding functionality to objects from the wire. Originally this was to make it easier for people when they
console.log()
objects but additionally it seems we get a per object size reduction from2,324 bytes
per object in this test bed to1,332 bytes
. This does increase construction of the objects by13%
which seems reasonable.What does it cost us?
We lose a few things in this exchange:
$updateInternalValues()
and have it reflect across any object that was$as()
'd. OSDK objects effectively become immutable in this scenario. I think this is okay, it just removes some future ideas I had.What else did we gain?
I think the code is a bit easier to reason about after we remove the proxies. Also the bundled minified client for nodejs (via esbuild) is reduced by about 4k in bytes.
Other notes
In a separate repo where I originally experimented with this, retrieving properties from a proxy vs directly on an object was about 2x as expensive. So for anyone iterating over all of the objects returned, the 13% construction time increase would be saved on property access.
Detailed benchmark info
On my machine,
createObjects.ts
benchmark stats prior (1b60b3d):So per object in the test data we were paying about
232406548.8/1000/100 ~= 2,324 bytes
per object.On my machine,
createObjects.ts
benchmark stats after:133270992.8/1000/100 ~= 1,332 bytes
per object.