Skip to content

[SDK-707] Replace request AsyncTask with SDK executor - #1096

Open
franco-zalamena-iterable wants to merge 6 commits into
feature/sdk-706-deeplink-redirect-executorfrom
feature/sdk-707-request-executor
Open

franco-zalamena-iterable wants to merge 6 commits into
feature/sdk-706-deeplink-redirect-executorfrom
feature/sdk-707-request-executor

Conversation

@franco-zalamena-iterable

@franco-zalamena-iterable franco-zalamena-iterable commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

📝 Summary

Moves API request execution from AsyncTask to an SDK-owned serial dispatcher while preserving callback threading and existing retry behavior.

🎟️ Jira Ticket: SDK-707

📖 Description

This stacked PR builds on SDK-706 and:

  • Replaces IterableRequestTask AsyncTask execution with a package-private IterableRequestDispatcher.
  • Serializes online, offline, delayed, and JWT retry requests on the SDK-owned request executor.
  • Delivers API response callbacks on the main thread.
  • Preserves the existing Android JWT retry contract: retried requests retain the legacy callback but do not reattach modern callbacks or the offline processor marker.
  • Adds readable behavior-focused Kotlin tests and an inline dispatcher test harness so tests do not depend on timing.

🧪 How to test?

  • ./gradlew --no-daemon :iterableapi:testDebugUnitTest :iterableapi-ui:testDebugUnitTest
  • ./gradlew --no-daemon :iterableapi:checkstyle
  • Review IterableApiRequestExecutionTest, IterableRequestDispatcherTest, and IterableRequestTaskTest for the documented production request flow.

The standalone inbox-customization sample remains blocked by its pre-existing Kotlin 1.8 incompatibility with the data object declaration in iterableapi-ui.

🧾 Changelog

Updated the existing Unreleased executor entry to include API requests and API response callback threading.

📹 Loom recording if applicable

Not applicable.

🐞 Github Issues solved

None.

📚 Docs PR if applicable

No documentation PR required.

@rtlsilva rtlsilva left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ticket says the full suite should contain no ShadowPausedAsyncTask references, but this head still has six references in BaseTest, IterableApiIntegrationTest, and IterableApiGetAndTrackDeepLinkTest.

Suggest removing the remaining shadow dependencies and synchronizing those tests through the new request seam or the appropriate executor/looper primitives.

healthMonitor,
classification);
taskScheduler = new TaskScheduler(taskStorage, taskRunner);
taskScheduler = new TaskScheduler(taskStorage, taskRunner, requestDispatcher);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dispatcher is passed to TaskScheduler, but stored requests do not use it: IterableTaskRunner still invokes IterableRequestTask.executeApiRequest() directly on its own HandlerThread. A persisted request can therefore overlap with or be overtaken by immediate requests running on IterableExecutors.serial(), so this PR's stated serialization of offline requests does not hold.

Suggest routing task-runner network execution through the same request queue and covering stored-versus-immediate ordering.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stored requests now use an SDK dispatcher, but the shared-ordering part of this thread has regressed in b6d0d841: immediate requests in offline mode use requestDispatchers.online() (now an 8-thread pool) while stored tasks use the separate requestDispatchers.offline() lane, and the submission-order test was removed. These paths can overlap or overtake one another again, and the PR description still says these requests are serialized.

This also isn't quite the pre-migration behaviour: on master, offline-mode immediate requests went through AsyncTask.execute() (the serial executor), and only OnlineRequestProcessor used THREAD_POOL_EXECUTOR. Routing them to online() makes them concurrent with each other as well.

If matching pre-migration concurrency is the goal, suggest keeping offline-mode immediate requests serial, updating the PR contract, and adding coverage for the intended cross-lane behavior.
Otherwise, suggest keeping both paths on one ordered dispatcher.

@franco-zalamena-iterable

Copy link
Copy Markdown
Contributor Author

Review feedback is addressed in ffe5431:

  • Restored IterableApiIntegrationTest to the production dispatcher.
  • Synchronized the callback-driven automatic push-registration test through the main looper.
  • Migrated IterableApiResponseTest to IterableRequestDispatcher; Android test sources compile.
  • Documented the inline request seam callback contract.
  • Routed persisted offline requests through the same serial dispatcher as immediate requests, with stored-versus-immediate ordering coverage and an in-flight guard.
  • Removed all ShadowPausedAsyncTask references from the suite.

Verification on this commit:

  • SDK-707 behavior and regression tests pass in the 764-test core run. The sole local failure is the pre-existing external-image IterableNotificationTest.testNotificationImage; 21 tests are skipped.
  • iterableapi-ui unit tests pass.
  • lintDebug, checkstyle, iterableapi-ui assembleDebug, app unit tests, and Android-test compilation pass.
  • The standalone sample reaches iterableapi-ui compilation and remains blocked by its existing Kotlin 1.8 versus data object language-version mismatch.

Requesting another review.

@franco-zalamena-iterable
franco-zalamena-iterable force-pushed the feature/sdk-707-request-executor branch from ffe5431 to e697ce6 Compare September 23, 2026 12:54
@franco-zalamena-iterable

Copy link
Copy Markdown
Contributor Author

Updated SDK-707 after the stack rebase, preserving all prior review fixes and addressing the concurrency concern:

  • Ordinary online API requests use a bounded concurrent SDK pool.
  • Push requests stay on the dedicated serial push lane.
  • Offline immediate and persisted requests share a dedicated serial offline lane.
  • Deep-link work remains on its separate SDK-706 lane.
  • Callbacks remain on main, and retries retain the originating dispatcher.

New focused coverage proves that the client routes ordinary and push requests to the correct lanes, a blocked online request does not serialize a second online request, and blocked online work does not delay push work. The synchronization uses latches with no sleeps.

Verification:

  • Focused executor, request, retry, offline, and task-runner suites pass.
  • Full core run: 770 passed, 21 skipped; only the pre-existing external IterableNotificationTest.testNotificationImage failed.
  • UI unit tests pass.
  • Lint, Checkstyle, UI assemble, root app unit tests, and Android-test source compilation pass.
  • The standalone sample remains blocked by its existing Kotlin 1.8 versus data-object language-version mismatch.

CI is currently running on e697ce6. Ready for another review.

@franco-zalamena-iterable

Copy link
Copy Markdown
Contributor Author

Follow-up from the first CI run: the new concurrency and routing tests passed. CI exposed a separate Mockito spy race in IterableFirebaseMessagingServiceTest, where a pending initialization callback could call getDebugMode while the test was stubbing getInAppManager. Commit 2f34558 now drains initialization callbacks before creating the spy and uses safe doReturn stubbing. The affected service tests and executor tests pass locally; replacement CI is running.

@franco-zalamena-iterable

Copy link
Copy Markdown
Contributor Author

Final CI follow-up for 2f34558:

  • Check, changelog, Java analysis, CodeQL, BCIT, and instrumentation all pass.
  • The callback-driven Mockito race no longer appears.
  • The unit job ran 771 tests with only the repository-wide external IterableNotificationTest.testNotificationImage failure; all SDK-707 executor, routing, request, retry, offline, and service tests passed.

The branch is now waiting only on reviewer approval and the known external image-test failure.

@rtlsilva rtlsilva left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new comments on this PR, but waiting for ancestor PRs #1093 and #1094 for a final review before approving.

@jferrao-itrbl jferrao-itrbl left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @franco-zalamena-iterable . LGTM.

It might be worth checking the failing test though it seems unrelated with these changes.

@joaodordio
joaodordio requested a review from a team September 25, 2026 10:18
@franco-zalamena-iterable
franco-zalamena-iterable force-pushed the feature/sdk-707-request-executor branch from 2f34558 to 7ca9382 Compare October 2, 2026 14:03
@franco-zalamena-iterable
franco-zalamena-iterable force-pushed the feature/sdk-707-request-executor branch from 7ca9382 to b6d0d84 Compare October 6, 2026 09:00

@rtlsilva rtlsilva left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reopened a discussion, no other findings.

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.

3 participants