Plugin settings are spuriously reset to defaults

Answered

I noticed that starting with 2020.1 my plugin's settings get sometimes reset to defaults. 

It will happen most often after the plugin is updated or after some VCS operations.

I have not been able to consistently reproduce this but it happens often enough.

This all started with the code changes to dynamic plugins and was not an issue before that.

Any ideas on how I can go about catching the conditions when this occurs?

6 comments
Comment actions Permalink

Anything in idea.log? Standard @State-settings with service?

0
Comment actions Permalink

Thanks, Yann.

I added debug log tracing to the code and realized that through all the settings migration and splitting of basic and enhanced settings resulted in possible service creation and component state load/save racing conditions.

Basically, plugin project settings appear as a single class to the code but contain extension settings which are persisted by different PersistentStateComponents.

During service creation of the project persistence component, extension persistence component(s) are instantiated to ensure enhanced settings are loaded and persisted when modified.

The way I had it implemented, it caused the enhanced persistence components to try and instantiate the main project settings persistence component service. To avoid trying to create a service when it was being constructed, I put the extension initialization code after the main service is created.

Which for the first call to getInstance of the main settings would cause them to be loaded without the extensions. However, once the extensions are instantiated the settings would reflect the correct values.

I am still not clear of the exact cause of what caused it to fail only sometimes. ie. Why it worked by accident at all, but suspect it was related to loadState being called on service instantiation and loadState being followed by getState in the IDE. So if the getState for the main component happened before the extensions had a chance to load, it would cause settings for these values to be reset to defaults.

Once I fixed the code to not request the main project settings service but instead pass the service instance which was being created to the extension, it appears to work consistently.

0
Comment actions Permalink

Thanks for the update!

0
Comment actions Permalink

Yann, the issue is more complex than I thought. My changes did reduce the incidence of settings being reset but did not eliminate it. I am able to reproduce this on my OSS plugin project with gradle buiild. On project open, maybe 20% of the time, especially when there is another project already open, the settings get reset after project initialization.

I added debug log traces on getState() and loadState(), including dumping the incoming state for loadState with a dump of the result state XML after loading. No issues on this front.

On the getState() I am seeing something very strange. As I open the project I am seeing loadState with correct values followed by multiple getState calls with all settings being right.

Then after the project gradle build is synced (not built but project settings synced on project open) or VCS is initialized (the settings XML is under VCS) I suddenly see getState result in completely wrong settings without any intervening loadState calls. Immediately preceding getState calls return the right settings and wham the next one is messed up.

I can see the effect on the file. When the settings are changed the parsing is messed up because the parser settings become wrong.

As far as I know the settings in the plugin are only changed though Configurables, Inspections or Actions. I am not expecting any changes to settings only reads.

I will add more log traces to see what is going on. All the async and layers of code is not making it easy. I might have to throw together a quick log inspection app to allow me to focus on traces of interest. The SNAPSHOT build I am using has a ton of repeated exceptions, amongst unrelated or relevant log traces, getting in the way:

java.lang.ClassCastException: class com.intellij.ui.IconWrapperWithToolTip cannot be cast to class com.intellij.openapi.util.ScalableIcon (com.intellij.ui.IconWrapperWithToolTip and com.intellij.openapi.util.ScalableIcon are in unnamed module of loader com.intellij.ide.BootstrapClassLoaderUtil$TransformingLoader @553f17c)
at com.intellij.execution.console.IdeConsoleRootType.substituteIcon(IdeConsoleRootType.java:43)

 

0
Comment actions Permalink

When I instrumented the code with detailed logs, the problem disappeared. Turning off the debug logs would cause it to intermittently reappear.

This led me to realize that it is possibly caused by simultaneous loading of state for main project settings and enhanced settings from different threads, which in the code I did not consider.

I synchronized the load operations on the project settings instance so only one loader could be active.

After the change I am not able to reproduce the error. I will keep my fingers crossed.

1
Comment actions Permalink

All that log tracing finally yielded results. I added traces to dump a stack trace to the log when settings reset condition is detected. This way logging did not affect timing and showed responsible code. In short order triggered and pointed to responsible code.

I discovered some really old code, which had no purpose for years but was never cleaned out. This code could modify some settings, instead of treating them as read-only.

In the end, the cause was old code which should have been cleaned up before it broke. :O

1

Please sign in to leave a comment.