Skip to content
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

fix: Clean up how objects are managed when they are fetched #4909

Closed
wants to merge 20 commits into from

Conversation

mofojed
Copy link
Member

@mofojed mofojed commented Dec 2, 2023

  • Clean up memory leak in JsPartitionedTable, JsFigure, PandasTable with widgets not being closed after being used
  • Add a takeTicket method to JsWidgetExportedObject to make it clear when ownership of the ticket has been passed on
  • Close exported objects that are still owned when closing a widget
  • Tested using the steps and scripts provided in the ticket, as well as testing with creating some tables and opening/closing them in the UI
  • Fixes Widgets cause leaks, do not release all resources #4903

- Previously, exported objects were not handling their fetched objects correctly
  - Tables were not using copies, only widgets were
  - The parent ticket was not being released in many cases (TreeTable, PartitonedTable, PandasTable)
- Refactored so exported objects always follow the same path
  - Just call connection.getObject, which always creates a new ticket for the object
  - Still need to close the widget so it closes the exported objects
- Test by adding debug logging for SessionState:
```py
import jpy
logger = jpy.get_type('org.slf4j.LoggerFactory').getLogger('io.deephaven.server.session.SessionState')
jpy.cast(logger, jpy.get_type('ch.qos.logback.classic.Logger')).setLevel(jpy.get_type('ch.qos.logback.classic.Level').DEBUG)
```
- Now it doesn't need to do the extra round trip
- Have the exported object "own" the ticket until it is taken from it
- Clean up memory leak in JsPartitionedTable, JsFigure, PandasTable with widgets not being closed after being used
- Add a `takeTicket` method to JsWidgetExportedObject to make it clear when ownership of the ticket has been passed on
- Close exported objects that are still owned when closing a widget
- Tested using the steps and scripts provided in the ticket, as well as testing with creating some tables and opening/closing them in the UI
- Fixes deephaven#4903
@mofojed mofojed added NoDocumentationNeeded NoReleaseNotesNeeded No release notes are needed. labels Dec 2, 2023
@mofojed mofojed requested a review from niloc132 December 2, 2023 05:37
@mofojed mofojed self-assigned this Dec 2, 2023
takeOwnership = false;
}
if (takeOwnership) {
return this.connection.getObject(takeTicket());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this means that raced calls to fetch(true) and fetch(false) are going to intermittently fail - probably need some clearer docs on this and other general usage of ownership.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was debating just having a separate method take instead of making it a boolean.
Really you should only be calling one or the other (true or false) depending on how you're managing the object, I wouldn't expect you'd want to call both.
In any case, I can add a verifyIsOwner() here in the false case, and throw if it's been called after a fetch(false) so it reduces a race that way, but you could still race fetch(false) then fetch(true) I suppose.

}
return this.connection.getObject(
new JsVariableDefinition(ticket.getType(), null, ticket.getTicket().getTicket_asB64(), null));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this works - VariableDefinition is meant for scope tickets...

Will think on it more, but I think I'd prefer we decompose this (or provide an interface and second impl) to let this be reexported-and-fetched.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that was already there. Looks like I added this back when implementing JsWidget: #1888 (comment)
I may have misinterpreted that comment then.
Should be able to refactor the WorkerConnection#getObject methods; the only part of the JsVariableDefinition that is actually used is the type and the id used in exportScopeTicket

- Can release the ticket of a widget automatically if the server closes the object - no way to reconnect (currently)
- Remove unnecessary closes
* @param refetch Whether to refetch the widget before resolving or not
* @return Promise of the widget
*/
public Promise<JsWidget> getWidget(JsVariableDefinition varDef, boolean refetch) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of "refetch" could we use something like "export" or "re-export"? Most of the time "Fetch" means to get the object from the server (usually by name), but this isn't doing that again

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refetch in this case is triggering widget.refetch(), which was already named... but essentially is restarting the stream and fetching the data. I think the reexport name probably doesn't fit here either, and refetch makes more sense (or JsWidget.refetch should be renamed?). Thoughts?

mofojed and others added 3 commits December 8, 2023 12:47
…rConnection.java

Co-authored-by: Colin Alworth <colin@vertispan.com>
…t/JsWidget.java

Co-authored-by: Colin Alworth <colin@vertispan.com>
- Made many methods private instead of public
- Renamed methods to `getExported*` to make it clear they are for server-side exports
- Changed default method of `JsWidgetExportedObject.fetch` so this is not a breaking change
@mofojed mofojed requested a review from niloc132 December 8, 2023 18:07
@mofojed
Copy link
Member Author

mofojed commented Dec 8, 2023

Rollups are screwed up, need to fix...

- JsTreeTable was using the ticket from the widget directly, which doesn't work now if the widget releases it's ticket when the stream disconnects (fetch-only widgets)
- Need to export the table as an exportedObject so we don't release it entirely
- Will need Colin to review, this seems a little strange.
@mofojed
Copy link
Member Author

mofojed commented Dec 8, 2023

@niloc132 So the issue now with releasing the widget ticket when the stream ends (as suggested in our previous discussion for fetch-only widgets) actually breaks JsTreeTable implementation, as it's using the ticket right from the widget for the hierarchical table??

exportRequest.setHierarchicalTableId(widget.getTicket());

So I changed HierarchicalTableTypePlugin to export a reference to the HierarchicalTable as an exported object... but I think JsPartitionedTable still has a problem... they're not expecting the widget ticket to be released after the widget has been fetched, which is at odds with what we're doing with Pandas (I'm actually not sure where the Pandas plugin is/how that works?).

I favour what I had previously, where the consumer needs to explicitly close the widget when they're done with it to release the ticket, rather than implicitly releasing the ticket when the stream ends... thoughts?

@mofojed
Copy link
Member Author

mofojed commented Dec 12, 2023

Closed in favour of #4939

@mofojed mofojed closed this Dec 12, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Dec 12, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Widgets cause leaks, do not release all resources
2 participants