Non-responsive UI Thread on client randomly - interpretation of json files?

I have a internet facing gateway that runs a Vision application that people in my company use in office / at home.

Normally works well enough but today an coworker told me it kept freezing on them. The first main window that opens has a Tree View that is heavy on the data, 15,000+ rows, and the application froze before the tree was visually populated with any data so I suspect it has something to do with amount of data being transferred over internet. Closing and reopening the application for him on his computer, it didn't freeze for me (of course lol). However, I did get two non-responsive UI warnings in the logger and I hope they might provide some insight as to what went wrong last time though I don't know how to interpret this json for issues so I am attaching the two files here and hopefully someone can point me in the right direction. Any ideas what was causing the non-responsiveness?

NonResponsiveEdt-2023-09-05_120815.json (47.1 KB)
NonResponsiveEdt-2023-09-05_120724.json (47.2 KB)

1 Like

Learning time:
It's just an automatic thread dump (Kindling is great for viewing these :slight_smile: ) Since you're experiencing UI hangups, you can just go to the AWT-EventQueue-0 thread (that's Swing's "event dispatch thread").

So:


java.desktop@11.0.18/javax.swing.plaf.synth.Region.getRegion(Unknown Source)
java.desktop@11.0.18/javax.swing.plaf.synth.SynthLookAndFeel.getRegion(Unknown Source)
java.desktop@11.0.18/javax.swing.plaf.synth.SynthContext.getContext(Unknown Source)
java.desktop@11.0.18/javax.swing.plaf.synth.SynthLabelUI.getContext(Unknown Source)
java.desktop@11.0.18/javax.swing.plaf.synth.SynthLabelUI.getContext(Unknown Source)
de.javasoft.plaf.synthetica.SyntheticaDefaultLookup.getContext(SyntheticaDefaultLookup.java:190)
de.javasoft.plaf.synthetica.SyntheticaDefaultLookup.getDefault(SyntheticaDefaultLookup.java:173)
java.desktop@11.0.18/sun.swing.DefaultLookup.get(Unknown Source)
java.desktop@11.0.18/sun.swing.DefaultLookup.getColor(Unknown Source)
java.desktop@11.0.18/sun.swing.DefaultLookup.getColor(Unknown Source)
java.desktop@11.0.18/javax.swing.tree.DefaultTreeCellRenderer.updateUI(Unknown Source)
java.desktop@11.0.18/javax.swing.JLabel.<init>(Unknown Source)
java.desktop@11.0.18/javax.swing.JLabel.<init>(Unknown Source)
java.desktop@11.0.18/javax.swing.tree.DefaultTreeCellRenderer.<init>(Unknown Source)
com.inductiveautomation.plaf.TreeCellRenderer.<init>(TreeCellRenderer.java:27)
java.base@11.0.18/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
java.base@11.0.18/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
java.base@11.0.18/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
java.base@11.0.18/java.lang.reflect.Constructor.newInstance(Unknown Source)
de.javasoft.plaf.synthetica.StyleFactory$22.propertyChange(StyleFactory.java:1528)
java.desktop@11.0.18/java.beans.PropertyChangeSupport.fire(Unknown Source)
java.desktop@11.0.18/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
java.desktop@11.0.18/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
java.desktop@11.0.18/java.awt.Component.firePropertyChange(Unknown Source)
java.desktop@11.0.18/javax.swing.JTree.setCellRenderer(Unknown Source)
java.desktop@11.0.18/javax.swing.plaf.basic.BasicTreeUI.updateRenderer(Unknown Source)
java.desktop@11.0.18/javax.swing.plaf.basic.BasicTreeUI.completeUIInstall(Unknown Source)
java.desktop@11.0.18/javax.swing.plaf.basic.BasicTreeUI.installUI(Unknown Source)
java.desktop@11.0.18/javax.swing.JComponent.setUI(Unknown Source)
java.desktop@11.0.18/javax.swing.JTree.setUI(Unknown Source)
java.desktop@11.0.18/javax.swing.JTree.updateUI(Unknown Source)
java.desktop@11.0.18/javax.swing.JTree.<init>(Unknown Source)
java.desktop@11.0.18/javax.swing.JTree.<init>(Unknown Source)
com.inductiveautomation.factorypmi.application.components.PMITreeView$1.<init>(PMITreeView.java:124)
com.inductiveautomation.factorypmi.application.components.PMITreeView.<init>(PMITreeView.java:124)
java.base@11.0.18/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
java.base@11.0.18/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
java.base@11.0.18/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
java.base@11.0.18/java.lang.reflect.Constructor.newInstance(Unknown Source)
java.base@11.0.18/java.lang.Class.newInstance(Unknown Source)
com.inductiveautomation.factorypmi.application.xmlserialization.ComponentDeserializationHandler.startSubElement(ComponentDeserializationHandler.java:77)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer$ParseContext.onElementStart(XMLDeserializer.java:876)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:151)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.readElement(BinaryParser.java:157)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.BinaryParser.parse(BinaryParser.java:51)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer.deserializeBinary(XMLDeserializer.java:367)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer.deserialize(XMLDeserializer.java:277)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer.deserialize(XMLDeserializer.java:257)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer.deserialize(XMLDeserializer.java:212)
com.inductiveautomation.ignition.common.xmlserialization.deserialization.XMLDeserializer.deserialize(XMLDeserializer.java:181)
com.inductiveautomation.factorypmi.application.runtime.ClientContextImpl.deserialize(ClientContextImpl.java:208)
com.inductiveautomation.factorypmi.application.model.TemplateManager.getTemplateInstance(TemplateManager.java:167)
com.inductiveautomation.factorypmi.application.components.template.TemplateHolder.loadTemplate(TemplateHolder.java:228)
com.inductiveautomation.factorypmi.application.components.template.TemplateHolder.initTemplate(TemplateHolder.java:514)
com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor$TemplateHolderVisitor.visit(ComponentVisitor.java:242)
com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor.walk(ComponentVisitor.java:95)
com.inductiveautomation.factorypmi.application.components.util.ComponentVisitor.walk(ComponentVisitor.java:73)
com.inductiveautomation.factorypmi.application.FPMIWindow.initializeUIRoot(FPMIWindow.java:323)
com.inductiveautomation.factorypmi.application.FPMIWindow.initialize(FPMIWindow.java:284)
com.inductiveautomation.factorypmi.application.VisionDesktop$RuntimeWindowOpener.openWindow(VisionDesktop.java:540)
com.inductiveautomation.factorypmi.application.VisionDesktop.openWindow(VisionDesktop.java:192)
com.inductiveautomation.factorypmi.application.VisionDesktop.openWindow(VisionDesktop.java:184)
com.inductiveautomation.factorypmi.application.FPMIApp.startup(FPMIApp.java:523)
com.inductiveautomation.factorypmi.application.runtime.ClientPanel.lambda$startupApp$8(ClientPanel.java:912)
com.inductiveautomation.factorypmi.application.runtime.ClientPanel$$Lambda$594/0x0000000100bccc40.run(Unknown Source)
java.desktop@11.0.18/java.awt.event.InvocationEvent.dispatch(Unknown Source)
java.desktop@11.0.18/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
java.desktop@11.0.18/java.awt.EventQueue$4.run(Unknown Source)
java.desktop@11.0.18/java.awt.EventQueue$4.run(Unknown Source)
java.base@11.0.18/java.security.AccessController.doPrivileged(Native Method)
java.base@11.0.18/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
java.desktop@11.0.18/java.awt.EventQueue.dispatchEvent(Unknown Source)
java.desktop@11.0.18/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
java.desktop@11.0.18/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
java.desktop@11.0.18/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
java.desktop@11.0.18/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
java.desktop@11.0.18/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
java.desktop@11.0.18/java.awt.EventDispatchThread.run(Unknown Source)

You're right - it's the tree trying to render 15000 items. That's just an expensive operation.

1 Like

Appreciate the lesson - good to know that IS the issue. Now to figure out a better way of handling this in general. I already have it as a named query with a reasonably long caching time but it is a huge result set and a not so performant query in general that rarely needs to re-run.

Maybe it should just be on a gateway memory tag and used in that way? Though as I recall a while back I think you mentioned tag's don't have good promises or something to that effect. I'll have to play around with it. This is the first time it's really given me an issue so I may not change anything for the time being, but it would be nice to have a plan to optimize the performance.

If you don't serialize the dataset (if you're already opting out, ignore this) then initial window construction should at least be fast - since it'll be an empty tree.

Loading 15000 items into a tree shouldn't be expensive (on the UI thread) on its own, because the "work" is just converting the dataset into a "tree" model, which is relatively cheap. Painting the UI elements should happen on demand only when the tree is expanded (unless you have auto-expand on for a 15000 element tree. If you do, for shame :slight_smile:)

I don't think I'm doing anything to the dataset re serializing - the data (items) property of the tree is directly bound to a named query that has caching enabled and I do nothing else. Sometimes I need to tell the tree so I clear the named query cache and refresh the data binding but that is all. That is true I do see the window and an empty tree relatively quick.

I'll take a look at the query first. One of my first tasks at this job was optimizing it, and while I did improve over the first iteration, it is certainly not optimized really optimized lol.

image
Is 'Retain Rows' set to false on the binding?

It is false.

A wrinkle I forgot about. I misspoke, the tree.data property is not directly bound to the named query, but a table that is invisible is using the NQ for it's data, and the tree.items is referencing the table.data for it's data. This is done so that the .propertiesLoading of the table can be used to animate a loading image. Don't know if the referencing of another components data property changes the answer regarding serialization or not.

Still going to take a second crack at the query, I know it's slow and the tree can't populate if it's still just waiting on the DB.