-
Notifications
You must be signed in to change notification settings - Fork 267
OSConsistencyManager & IAM fetch read-your-write consistency implementation
#1486
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
fa3c750
OSReadYourWriteData Implementation
eb45946
OSConsistencyManager & related classes
05d327d
IamFetch consistency use-case implementation
6f40409
Update project.pbxproj
b07df39
Move `getTimeFocusedElapsed` from `OneSignalTracker` to `OSSessionMan…
e7d65f6
Modify `OSRequestGetInAppMessages` method to include new header values
9ac1053
OSMessagingController Retry Logic
d09d464
Update executors to set offsets on consistency manager
924d8a1
Flush delta queue on start
b8aeed2
Update UnitTestApp TestPlan xctestplan
82e468b
Update OSIamFetchReadyCondition to sharedInstance to waist on sub update
4f7a9dd
Update OneSignalClient to include headers
663edc1
Run swiftlint --fix
75eab0f
Clarifying comment re: why we track RYW tokens for user create as well
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
OSConsistencyManager & related classes
Motivation: Manages (kafka) offsets that function as read-your-write tokens for more accurate segment membership calculation. The manager works based on conditions & offsets. Offsets are stored in a nested map indexed by a unique id (e.g. `onesignalId`) and offset key (e.g. `USER_UPDATE`). This allows us to track offsets on a per-user basis (e.g. handle switching users). Conditions work by creating a blocking mechanism with customizable offset retrieval until a pre-defined condition is met (e.g. at least two specific offsets are available). OSCondition interface: allows extensibility for future applications to control offset blocking mechanism in consistency use-cases.
- Loading branch information
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/Consistency/OSCondition.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // | ||
| // OSCondition.swift | ||
| // OneSignalOSCore | ||
| // | ||
| // Created by Rodrigo Gomez-Palacio on 9/10/24. | ||
| // Copyright © 2024 OneSignal. All rights reserved. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| @objc public protocol OSCondition: AnyObject { | ||
| // Each conforming class will provide its unique ID | ||
| var conditionId: String { get } | ||
| func isMet(indexedTokens: [String: [NSNumber: OSReadYourWriteData]]) -> Bool | ||
| func getNewestToken(indexedTokens: [String: [NSNumber: OSReadYourWriteData]]) -> OSReadYourWriteData? | ||
| } | ||
12 changes: 12 additions & 0 deletions
12
iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/Consistency/OSConsistencyKeyEnum.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // | ||
| // OSConsistencyKeyEnum.swift | ||
| // OneSignalOSCore | ||
| // | ||
| // Created by Rodrigo Gomez-Palacio on 9/10/24. | ||
| // Copyright © 2024 OneSignal. All rights reserved. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| // Protocol for enums with Int raw values. | ||
| public protocol OSConsistencyKeyEnum: RawRepresentable where RawValue == Int { } |
88 changes: 88 additions & 0 deletions
88
iOS_SDK/OneSignalSDK/OneSignalOSCore/Source/Consistency/OSConsistencyManager.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| // | ||
| // OSConsistencyManager.swift | ||
| // OneSignalOSCore | ||
| // | ||
| // Created by Rodrigo Gomez-Palacio on 9/10/24. | ||
| // Copyright © 2024 OneSignal. All rights reserved. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| @objc public class OSConsistencyManager: NSObject { | ||
| // Singleton instance | ||
| @objc public static let shared = OSConsistencyManager() | ||
|
|
||
| private let queue = DispatchQueue(label: "com.consistencyManager.queue") | ||
| private var indexedTokens: [String: [NSNumber: OSReadYourWriteData]] = [:] | ||
| private var indexedConditions: [String: [(OSCondition, DispatchSemaphore)]] = [:] // Index conditions by condition id | ||
|
|
||
| // Private initializer to prevent multiple instances | ||
| private override init() {} | ||
|
|
||
| // Used for testing | ||
| public func reset() { | ||
| indexedTokens = [:] | ||
| indexedConditions = [:] | ||
| } | ||
|
|
||
| // Function to set the token in a thread-safe manner | ||
| public func setRywTokenAndDelay(id: String, key: any OSConsistencyKeyEnum, value: OSReadYourWriteData) { | ||
| queue.sync { | ||
| let nsKey = NSNumber(value: key.rawValue) | ||
| if self.indexedTokens[id] == nil { | ||
| self.indexedTokens[id] = [:] | ||
| } | ||
| self.indexedTokens[id]?[nsKey] = value | ||
| self.checkConditionsAndComplete(forId: id) // Only check conditions for this specific ID | ||
| } | ||
| } | ||
|
|
||
| // Register a condition and block the caller until the condition is met | ||
| @objc public func getRywTokenFromAwaitableCondition(_ condition: OSCondition, forId id: String) -> OSReadYourWriteData? { | ||
| let semaphore = DispatchSemaphore(value: 0) | ||
| queue.sync { | ||
| if self.conditions[id] == nil { | ||
| self.conditions[id] = [] | ||
| } | ||
| self.conditions[id]?.append((condition, semaphore)) | ||
| self.checkConditionsAndComplete(forId: id) | ||
| } | ||
| semaphore.wait() // Block until the condition is met | ||
| return queue.sync { | ||
| return condition.getNewestToken(indexedTokens: self.indexedTokens) | ||
| } | ||
| } | ||
|
|
||
| // Method to resolve conditions by condition ID (e.g. OSIamFetchReadyCondition.ID) | ||
| @objc public func resolveConditionsWithID(id: String) { | ||
| guard let conditionList = conditions[id] else { return } | ||
| var completedConditions: [(OSCondition, DispatchSemaphore)] = [] | ||
| for (condition, semaphore) in conditionList { | ||
| if (condition.conditionId == id) { | ||
| semaphore.signal() | ||
| completedConditions.append((condition, semaphore)) | ||
| } | ||
| } | ||
| conditions[id]?.removeAll { condition, semaphore in | ||
| completedConditions.contains(where: { $0.0 === condition && $0.1 == semaphore }) | ||
| } | ||
| } | ||
|
|
||
| // Private method to check conditions for a specific id (unique ID like onesignalId) | ||
| private func checkConditionsAndComplete(forId id: String) { | ||
| guard let conditionList = conditions[id] else { return } | ||
| var completedConditions: [(OSCondition, DispatchSemaphore)] = [] | ||
| for (condition, semaphore) in conditionList { | ||
| if condition.isMet(indexedTokens: indexedTokens) { | ||
| print("Condition met for id: \(id)") | ||
rgomezp marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| semaphore.signal() | ||
| completedConditions.append((condition, semaphore)) | ||
| } else { | ||
| print("Condition not met for id: \(id)") | ||
| } | ||
| } | ||
| conditions[id]?.removeAll { condition, semaphore in | ||
| completedConditions.contains(where: { $0.0 === condition && $0.1 == semaphore }) | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.