Skip to content

Commit 6e24c3c

Browse files
authored
Merge pull request #2384 from digma-ai/improve-error-handling-in-AggressiveUpdateService
better error handling in AggressiveUpdateService Closes #2383
2 parents dea5cf7 + 2d5dc53 commit 6e24c3c

File tree

3 files changed

+43
-36
lines changed

3 files changed

+43
-36
lines changed

ide-common/src/main/java/org/digma/intellij/plugin/analytics/AnalyticsService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,11 @@ public void dispose() {
514514
* All calls to the AnalyticsProvider proxy must be wrapped by a call to this method.
515515
* this is purely catch exceptions and rethrow AnalyticsServiceException.
516516
*/
517+
/*
518+
Note: this method should throw only AnalyticsServiceException.
519+
the only case it will throw something else is on EDTAccessException or ReadAccessException which are severe errors
520+
that should turn a red error to user, and we must catch those during development or testing before release.
521+
*/
517522
private <T> T executeCatching(Supplier<T> tSupplier) throws AnalyticsServiceException {
518523
try {
519524
return tSupplier.get();

ide-common/src/main/java/org/digma/intellij/plugin/common/ExceptionUtils.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,7 @@ public static boolean isAnyConnectionException(@NotNull Throwable e) {
117117
}
118118

119119
public static boolean isConnectionException(@NotNull Throwable e) {
120-
121-
var ex = e.getCause();
122-
while (ex != null && !(isConnectionUnavailableException(ex))) {
123-
ex = ex.getCause();
124-
}
125-
return ex != null;
120+
return findConnectException(e) != null;
126121
}
127122

128123
public static boolean isConnectionUnavailableException(@NotNull Throwable exception) {
@@ -186,8 +181,7 @@ private static boolean is404PageNotFound(@NotNull Throwable exception) {
186181

187182

188183
public static boolean isSslConnectionException(@NotNull Throwable e) {
189-
var cause = findAssignableCause(SSLException.class, e);
190-
return cause != null;
184+
return findSslException(e) != null;
191185
}
192186

193187
@Nullable

ide-common/src/main/kotlin/org/digma/intellij/plugin/updates/AggressiveUpdateService.kt

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@ import org.apache.maven.artifact.versioning.ComparableVersion
1010
import org.digma.intellij.plugin.analytics.AnalyticsService
1111
import org.digma.intellij.plugin.analytics.AnalyticsServiceConnectionEvent
1212
import org.digma.intellij.plugin.analytics.ApiClientChangedEvent
13-
import org.digma.intellij.plugin.analytics.BackendInfoHolder
1413
import org.digma.intellij.plugin.common.DisposableAdaptor
1514
import org.digma.intellij.plugin.common.ExceptionUtils
1615
import org.digma.intellij.plugin.common.buildVersionRequest
1716
import org.digma.intellij.plugin.common.getPluginVersion
1817
import org.digma.intellij.plugin.common.isProjectValid
1918
import org.digma.intellij.plugin.common.newerThan
20-
import org.digma.intellij.plugin.common.runWIthRetry
2119
import org.digma.intellij.plugin.errorreporting.ErrorReporter
2220
import org.digma.intellij.plugin.log.Log
2321
import org.digma.intellij.plugin.model.rest.AboutResult
@@ -118,7 +116,9 @@ class AggressiveUpdateService(val project: Project) : DisposableAdaptor {
118116
}
119117

120118

121-
@Synchronized
119+
//startMonitoring is called from init while the object is constructed.
120+
//it should never be called concurrently because this is a project service and the platform guards against
121+
// concurrent creation of services.
122122
private fun startMonitoring() {
123123

124124
Log.log(logger::debug, "startMonitoring called")
@@ -128,7 +128,12 @@ class AggressiveUpdateService(val project: Project) : DisposableAdaptor {
128128
return
129129
}
130130

131-
disposingPeriodicTask("AggressiveUpdate.periodic", delayBetweenUpdatesSeconds.inWholeMilliseconds, true) {
131+
//this task is paused by the scheduler if there is no connection. It's useless to try to update without backend because there is no way to
132+
// know the backend version.
133+
//but because this service starts on project startup,and actually IDE startup, it may run once before ApiErrorHandler had the chance
134+
// to mark connection lost. if it will run when backend is not running there will be too many error reports in posthog,
135+
// so it should ignore no connection errors.
136+
disposingPeriodicTask("AggressiveUpdate.periodic", 10.seconds.inWholeMilliseconds, delayBetweenUpdatesSeconds.inWholeMilliseconds, true) {
132137
update()
133138
}
134139
}
@@ -140,42 +145,45 @@ class AggressiveUpdateService(val project: Project) : DisposableAdaptor {
140145
updateState()
141146
Log.log(logger::trace, "updating state on startup completed successfully")
142147
} catch (e: Throwable) {
143-
val message = ExceptionUtils.getNonEmptyMessage(e)
144-
Log.debugWithException(logger, e, "error in updateStateNow {}", message)
145-
errorReporter.reportError("AggressiveUpdateService.updateStateNow", e)
148+
//don't report connection errors , its useless and will report too many errors to posthog and the log
149+
if (!ExceptionUtils.isAnyConnectionException(e)) {
150+
val message = ExceptionUtils.getNonEmptyMessage(e)
151+
Log.debugWithException(logger, e, "error in update {}", message)
152+
errorReporter.reportError("AggressiveUpdateService.update", e)
153+
}
146154
}
147155
}
148156

149157

150158
private fun updateState() {
151-
152-
runWIthRetry({
153-
Log.log(logger::debug, "loading versions")
154-
val versions = buildVersions()
155-
Log.log(logger::debug, "loaded versions {}", versions)
156-
Log.log(logger::debug, "updating state")
157-
val prevUpdateState = updateStateRef.get().copy()
158-
update(versions)
159-
Log.log(logger::debug, "state updated. prev state: {}, new state: {}", prevUpdateState, updateStateRef)
160-
}, backOffMillis = 2000, maxRetries = 5)
159+
Log.log(logger::debug, "loading versions")
160+
val versions = buildVersions()
161+
Log.log(logger::debug, "loaded versions {}", versions)
162+
Log.log(logger::debug, "updating state")
163+
val prevUpdateState = updateStateRef.get().copy()
164+
update(versions)
165+
Log.log(logger::debug, "state updated. prev state: {}, new state: {}", prevUpdateState, updateStateRef)
161166
}
162167

163168

169+
//if there is no backend running this method will throw an AnalyticsServiceException
164170
private fun buildVersions(): Versions {
165-
try {
171+
return try {
166172
val versionsResponse = AnalyticsService.getInstance(project).getVersions(buildVersionRequest())
167173
reportVersionsErrorsIfNecessary(versionsResponse.errors)
168-
return Versions.fromVersionsResponse(versionsResponse)
174+
Versions.fromVersionsResponse(versionsResponse)
169175
} catch (e: Throwable) {
170-
val message = ExceptionUtils.getNonEmptyMessage(e)
171-
Log.debugWithException(logger, e, "error in buildVersions {}", message)
172-
errorReporter.reportError("AggressiveUpdateService.buildVersions", e)
173-
174-
//if getVersions failed try to get server version from getAbout
175-
val about = BackendInfoHolder.getInstance(project).getAbout()
176-
about?.let {
177-
return Versions.fromAboutResponse(it)
178-
} ?: throw RuntimeException("could not get backend info from getVersions or getAbout")
176+
//don't report connection errors , its useless and will report too many errors to posthog and the log
177+
if (!ExceptionUtils.isAnyConnectionException(e)) {
178+
val message = ExceptionUtils.getNonEmptyMessage(e)
179+
Log.debugWithException(logger, e, "error in buildVersions {}", message)
180+
errorReporter.reportError("AggressiveUpdateService.buildVersions", e)
181+
}
182+
183+
//if getVersions failed try to get server version from getAbout.
184+
//if there is no connection getAbout will throw an AnalyticsServiceException
185+
val about = AnalyticsService.getInstance(project).about
186+
Versions.fromAboutResponse(about)
179187
}
180188
}
181189

0 commit comments

Comments
 (0)