Skip to content

fix: Make setSessionId synchronous to prevent race with MPRoktLayout#735

Closed
nickolas-dimitrakas wants to merge 1 commit into
workstation/8.x-Maintenancefrom
fix/synchronous-set-session-id
Closed

fix: Make setSessionId synchronous to prevent race with MPRoktLayout#735
nickolas-dimitrakas wants to merge 1 commit into
workstation/8.x-Maintenancefrom
fix/synchronous-set-session-id

Conversation

@nickolas-dimitrakas
Copy link
Copy Markdown
Contributor

@nickolas-dimitrakas nickolas-dimitrakas commented Apr 9, 2026

Background

A race condition in setSessionId was causing web-to-native session handoff to fail on iOS. The method was delivered asynchronously via two dispatch_async(main) hops through forwardSDKCall, while MPRoktLayout reads the session synchronously from UserDefaults during execute. This meant the session ID was missing when both calls were made in quick succession. Android was unaffected because its setSessionId path is synchronous.

What Has Changed

  • Made setSessionId use the same direct kit access pattern that getSessionId already uses, bypassing forwardSDKCall and delivering the session ID synchronously to the kit
  • Added fallback to forwardSDKCall when kits haven't initialized yet, preserving existing queuing behavior
  • Replaced the old async testSetSessionIdForwardsToKitContainer test with three new tests covering synchronous delivery, forwardSDKCall bypass, and fallback behavior

Timeline

Click to see Sequence Diagram: setSessionId and MPRoktLayout Execute image

Checklist

  • I have performed a self-review of my own code.
  • I have made corresponding changes to the documentation.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have tested this locally.

setSessionId was using two async main queue hops (via forwardSDKCall)
before reaching the Rokt SDK, causing a race condition when followed
immediately by MPRoktLayout which reads the session synchronously.

Use the same direct kit access pattern as getSessionId, falling back
to forwardSDKCall only when kits haven't initialized yet.

Made-with: Cursor
@nickolas-dimitrakas nickolas-dimitrakas requested a review from a team as a code owner April 9, 2026 15:55
@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 9, 2026

PR Summary

Medium Risk
Changes MPRokt session propagation behavior from always-queued to sometimes synchronous, which can affect timing/order of calls and relies on runtime selector dispatch to the kit wrapper; small but behaviorally significant in production integrations.

Overview
Fixes a race where setSessionId could be applied too late by forwarding it synchronously to the active Rokt kit instance when available, instead of always queueing via forwardSDKCall.

Preserves existing behavior by falling back to the deferred forwardSDKCall path when the Rokt kit is not yet active, and adds unit tests asserting direct-kit delivery, synchronous timing, and the fallback queuing behavior.

Reviewed by Cursor Bugbot for commit c480e95. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 9, 2026

📦 SDK Size Impact Report

Measures how much the SDK adds to an app's size (with-SDK minus without-SDK).

Metric Target Branch This PR Change
App Bundle Impact 1.82 MB 1.82 MB +N/A
Executable Impact 896 bytes 896 bytes +N/A
XCFramework Size 9.51 MB 9.50 MB -4 KB

➡️ SDK size impact change is minimal.

Raw measurements

Target branch (workstation/8.x-Maintenance):

{"baseline_app_size_kb":84,"baseline_executable_size_bytes":75464,"with_sdk_app_size_kb":1948,"with_sdk_executable_size_bytes":76360,"sdk_impact_kb":1864,"sdk_executable_impact_bytes":896,"xcframework_size_kb":9736}

This PR:

{"baseline_app_size_kb":84,"baseline_executable_size_bytes":75464,"with_sdk_app_size_kb":1948,"with_sdk_executable_size_bytes":76360,"sdk_impact_kb":1864,"sdk_executable_impact_bytes":896,"xcframework_size_kb":9732}

NSArray<id<MPExtensionKitProtocol>> *activeKits =
[[MParticle sharedInstance].kitContainer_PRIVATE activeKitsRegistry];

for (id<MPExtensionKitProtocol> kitRegister in activeKits) {
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.

setSessionId is only used on rokt object. So iterating through other kits is not required.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

since its an array he has to iterate through until he finds the rokt kit.

}
}

MPILogDebug(@"MPRokt setSessionId - kit not available, queueing for deferred execution");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

So you're both setting it directly and then setting it throught the normal kit container way?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I'm still using the previous logic in the case that the kit is not configured yet

NSArray<id<MPExtensionKitProtocol>> *activeKits =
[[MParticle sharedInstance].kitContainer_PRIVATE activeKitsRegistry];

for (id<MPExtensionKitProtocol> kitRegister in activeKits) {
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.

We can extract loop in one method getRockKit, If we have not specific property to directly access kit

// Test helper class that simulates a kit with getSessionId and setSessionId methods
@interface MPRoktTestKitInstance : NSObject
@property (nonatomic, copy) NSString *sessionIdToReturn;
@property (nonatomic, copy) NSString *receivedSessionId;
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.

You can just create property sessionId instead additional getter and setter.

- (void)setSessionId:(NSString *)sessionId;
@end

@implementation MPRoktTestKitInstance
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.

In general about the test, shouldn't we create new test in swift?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Went for speed here, I will try to create the tests in swift if it is not too time consuming. Otherwise I can try to address this in a separate PR

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.

4 participants