Update of content from spawned thread issues (v23)

I have a complex display of multiple charts backed by occasionally relatively long-running database queries. I initially render each chart with a little animated spinner and fire off tasks to a little thread pool to execute the query and then replace the spinner with an actual chart of the results. Each task gets the current UI so that it can run ui.access when it needs to update the tree and let server push do its thing.
What I’ve found though is that sometimes the UI present when launching these tasks is a JavaScriptBootstrapUI instance, which disappears by the time the asynchronous updater is ready to run.
Pesudocode example:

public class LazyChart {
   public Component render() {
      var container = new Div();
      container.add(new Spinner());
      var ui = UI.getCurrent();   // this is sometimes instanceof JavaScriptBootstrapUI...
      runInThread(() -> {
         var results = doDBQuery();
         var chart = makeChart(results);
         ui.access(() -> {           // when ui was JavaScriptBootstrapUI, this often fails
             container.removeAll();
             container.add(chart);
             ui.push();
         });
      });
   }
}

What’s the solution here?

P.S. we’re currently running Vaadin (flow) 23.4.1. We’ve got a v24 update pending that I haven’t examined in detail yet.

Where is the render method called from?

render() gets called by onAttach() of LazyChart (which is itself a complex component).

Hmm… it seems like the UI is always JavaScriptBootstrapUI - but the ui.access call only fails some of the time.

What do you mean by “UI.access fails”? Is there any exception? Or simply the changes are not propagated to the client?

Forgive the question, but just to be sure, do you have enabled push with the @Push annotation?

  • the application is properly annotated with @Push and it generally works acceptably, this issue aside.
  • the longer the delay due to the query, the more likely it is to happen, which seriously interferes with the whole idea of asyncronous updates.
  • ui.access throws an exception:
com.vaadin.flow.component.UIDetachedException: null
        at com.vaadin.flow.component.UI.handleAccessDetach(UI.java:434) ~[flow-server-23.4.1.jar:23.4.1]
        at com.vaadin.flow.component.UI.access(UI.java:534) ~[flow-server-23.4.1.jar:23.4.1]
        at com.vaadin.flow.component.UI.access(UI.java:521) ~[flow-server-23.4.1.jar:23.4.1]

Obviously the change is also not propagated to the client.

I want to be clear that this does not happen every time - I would expect something like this if there was a forcible refresh or navigation, but nothing like that is happening here.

That’s really weird. It would probably not change anything, but, since render() is called from onAttach you can consider getting the UI from the event instead of using UI.getCurrent()

That’s a good idea, Thanks! I’ve updated my code and will see what happens.

I don’t know your thread architecture, but it’s recommend to also check for ui.session.lock before using access, sometimes it’s not required at all… see How to push updates to a Vaadin Flow user interface

I’m personally also a fan of ui.isClosing to ensure I don’t update my UI if it’s already in the process of being detached (which could explain your exception)

JavaScriptBootstrapUI might be related to how the current page was opened (directly vs link vs router link) … but I’m not sure anymore - this class was removed years ago. You should have a way smoother experience with V24.

the longer the delay due to the query, the more likely it is to happen

Reminds me of the issue @marcoc_753 has investigated some weeks ago where the push connection might be closed after 30s

What is the type of the ui instance when its not JavaScriptBootstrapUI?

Are you sure the UI isn’t actually detached? You could try adding a detach listener to the UI instance to detect if that’s the case. If that listener is fired unexpectedly, then you could put a breakpoint in it to see what it is that triggers the detach.