Skip to content

fix(analytics): keep polling alive when fetch throws a Throwable (SDK-86)#93

Open
tylerjroach wants to merge 1 commit into
masterfrom
fix/polling-throwable-defense
Open

fix(analytics): keep polling alive when fetch throws a Throwable (SDK-86)#93
tylerjroach wants to merge 1 commit into
masterfrom
fix/polling-throwable-defense

Conversation

@tylerjroach

Copy link
Copy Markdown
Contributor

Summary

ScheduledExecutorService#scheduleAtFixedRate permanently cancels future executions if the runnable throws an uncaught Throwable. fetchDefinitions() only catches Exception, so an Error (OutOfMemoryError, StackOverflowError, LinkageError, etc.) escaping would silently kill flag-definitions polling for the JVM's lifetime — and the thread factory has no UncaughtExceptionHandler to even surface it.

Wrap the scheduled runnable in a defensive try { fetchDefinitions(); } catch (Throwable t) { logger.log(SEVERE, ...) }. fetchDefinitions() keeps its Exception handling unchanged; the wrapper exists purely to swallow + log JVM-level escapes so the schedule keeps firing.

Context

Linear: SDK-86. Practical scope is narrow (JVM-level errors only) but the defense is one-line.

Test plan

  • Full test suite passes (173 tests, was 172, +1 new)
  • New testPollingSurvivesThrowableFromFetch arms an Error to escape the second httpGet call, then asserts the polling task continues to fire (callCount >= 3 after 2.5s with a 1s interval). Before the fix the scheduler would have cancelled the task on the Error and callCount would have stopped at 2.

ScheduledExecutorService#scheduleAtFixedRate permanently cancels
future executions if the runnable throws an uncaught Throwable.
fetchDefinitions only catches Exception, so an Error (OOM,
StackOverflowError, LinkageError, etc.) escaping would silently
kill flag-definitions polling for the JVM's lifetime.

Wrap the scheduled runnable in a defensive try/catch(Throwable).
fetchDefinitions keeps its Exception handling unchanged; the
wrapper exists purely to swallow + log JVM-level escapes so the
schedule keeps firing.

Linear: SDK-86

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tylerjroach tylerjroach requested review from a team and jakewski June 30, 2026 19:43
@linear-code

linear-code Bot commented Jun 30, 2026

Copy link
Copy Markdown

SDK-86

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge — the change is a single narrow defensive wrapper that leaves all existing logic untouched.

The change is minimal and well-targeted: one extra lambda wrapping one method call, with a new test that directly validates the scheduling-survives-Error behavior. No existing paths are altered.

No files require special attention.

Important Files Changed

Filename Overview
src/main/java/com/mixpanel/mixpanelapi/featureflags/provider/LocalFlagsProvider.java Wraps the scheduled fetchDefinitions() call in a try-catch (Throwable) to prevent ScheduledExecutorService from silently cancelling the polling task on JVM-level Errors; rest of the file unchanged.
src/test/java/com/mixpanel/mixpanelapi/featureflags/provider/LocalFlagsProviderTest.java Adds testPollingSurvivesThrowableFromFetch which arms an Error on the second httpGet call and asserts polling continues (callCount >= 3) after 2.5s; correct and well-structured.

Reviews (1): Last reviewed commit: "fix(analytics): keep polling alive when ..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant