Skip to content

[FME-4230] Events - Updated Segments #689

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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c67bcd6
Added logic to show updated flags. Tests pending.
MartinCardozo-SDK Jun 12, 2025
932ac8a
Tests updated
MartinCardozo-SDK Jun 12, 2025
06b39fe
Cleanup
MartinCardozo-SDK Jun 12, 2025
d1a3d93
Initial commit
MartinCardozo-SDK Jun 12, 2025
582192d
Marks
MartinCardozo-SDK Jun 15, 2025
4f8acea
Merge branch 'FME-4228-events-updatedFlag' into FME-4230-events-updat…
MartinCardozo-SDK Jun 15, 2025
f181aeb
Changing branch
MartinCardozo-SDK Jun 16, 2025
f600d62
Entities made public
MartinCardozo-SDK Jun 17, 2025
fd76335
Merge branch 'FME-4222-events-withMetadata' into FME-4230-events-upda…
MartinCardozo-SDK Jun 17, 2025
535196c
Merge branch 'FME-4222-events-withMetadata' into FME-4230-events-upda…
MartinCardozo-SDK Jun 17, 2025
b1c564b
Merge branch 'FME-4229-events-killedFlag' into FME-4230-events-update…
MartinCardozo-SDK Jun 25, 2025
0b9851b
Updated to new data structure
MartinCardozo-SDK Jun 25, 2025
745e02b
Merging
MartinCardozo-SDK Jun 26, 2025
cdfc70e
Merged
MartinCardozo-SDK Jun 28, 2025
2a39839
Tests passing
MartinCardozo-SDK Jun 30, 2025
88d03a8
Change
MartinCardozo-SDK Jun 30, 2025
02bc880
Merging
MartinCardozo-SDK Jun 30, 2025
8fd7fef
Changing
MartinCardozo-SDK Jun 30, 2025
e1f7a32
Changing
MartinCardozo-SDK Jun 30, 2025
74f545b
Merged branch
MartinCardozo-SDK Jun 30, 2025
6968ebe
Test passing
MartinCardozo-SDK Jun 30, 2025
b2a3985
Added test for built metadata passed to the EventsManager
MartinCardozo-SDK Jun 30, 2025
4a46025
Added test to check correct sending of metadata by key
MartinCardozo-SDK Jun 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 29 additions & 26 deletions Split/Events/SplitEventsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class DefaultSplitEventsManager: SplitEventsManager {
}
}

// MARK: Notifiers
func notifyInternalEvent(_ event: SplitInternalEvent, metadata: EventMetadata? = nil) {
let event = SplitInternalEventWithMetadata(event, metadata: metadata)

Expand All @@ -64,7 +65,8 @@ class DefaultSplitEventsManager: SplitEventsManager {
func notifyInternalEvent(_ event: SplitInternalEvent) {
notifyInternalEvent(event, metadata: nil)
}


// MARK: Registers
func register(event: SplitEventWithMetadata, task: SplitEventActionTask) {
let eventName = event.type.toString()
processQueue.async { [weak self] in
Expand All @@ -83,6 +85,7 @@ class DefaultSplitEventsManager: SplitEventsManager {
register(event: SplitEventWithMetadata(type: event, metadata: nil), task: task)
}

// MARK: Flow
func start() {
dataAccessQueue.sync {
if self.isStarted {
Expand Down Expand Up @@ -158,31 +161,31 @@ class DefaultSplitEventsManager: SplitEventsManager {
}
self.triggered.append(event.type)
switch event.type {
case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated:
if isTriggered(external: .sdkReady) {
trigger(event: .sdkUpdated, metadata: event.metadata)
continue
}
self.triggerSdkReadyIfNeeded()

case .mySegmentsLoadedFromCache, .myLargeSegmentsLoadedFromCache,
.splitsLoadedFromCache, .attributesLoadedFromCache:
Logger.v("Event \(event) triggered")
if isTriggered(internal: .splitsLoadedFromCache),
isTriggered(internal: .mySegmentsLoadedFromCache),
isTriggered(internal: .myLargeSegmentsLoadedFromCache),
isTriggered(internal: .attributesLoadedFromCache) {
trigger(event: .sdkReadyFromCache)
}
case .splitKilledNotification:
if isTriggered(external: .sdkReady) {
trigger(event: .sdkUpdated, metadata: event.metadata)
continue
}
case .sdkReadyTimeoutReached:
if !isTriggered(external: .sdkReady) {
trigger(event: .sdkReadyTimedOut)
}
case .splitsUpdated, .mySegmentsUpdated, .myLargeSegmentsUpdated:
if isTriggered(external: .sdkReady) {
trigger(event: .sdkUpdated, metadata: event.metadata)
continue
}
self.triggerSdkReadyIfNeeded()

case .mySegmentsLoadedFromCache, .myLargeSegmentsLoadedFromCache,
.splitsLoadedFromCache, .attributesLoadedFromCache:
Logger.v("Event \(event) triggered")
if isTriggered(internal: .splitsLoadedFromCache),
isTriggered(internal: .mySegmentsLoadedFromCache),
isTriggered(internal: .myLargeSegmentsLoadedFromCache),
isTriggered(internal: .attributesLoadedFromCache) {
trigger(event: .sdkReadyFromCache)
}
case .splitKilledNotification:
if isTriggered(external: .sdkReady) {
trigger(event: .sdkUpdated, metadata: event.metadata)
continue
}
case .sdkReadyTimeoutReached:
if !isTriggered(external: .sdkReady) {
trigger(event: .sdkReadyTimedOut)
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion Split/FetcherEngine/Refresh/ChangesChecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ protocol MySegmentsChangesChecker {
func mySegmentsHaveChanged(old: SegmentChange, new: SegmentChange) -> Bool
func mySegmentsHaveChanged(oldSegments: [Segment], newSegments: [Segment]) -> Bool
func mySegmentsHaveChanged(oldSegments: [String], newSegments: [String]) -> Bool
func getSegmentsDiff(oldSegments: [Segment], newSegments: [Segment]) -> [String]
}

struct DefaultMySegmentsChangesChecker: MySegmentsChangesChecker {
Expand All @@ -42,5 +43,8 @@ struct DefaultMySegmentsChangesChecker: MySegmentsChangesChecker {
return !(oldSegments.count == newSegments.count &&
oldSegments.sorted() == newSegments.sorted())
}


func getSegmentsDiff(oldSegments: [Segment], newSegments: [Segment]) -> [String] {
oldSegments.filter { !Set(newSegments.map { $0.name }).contains($0.name) }.map { $0.name }
}
}
13 changes: 9 additions & 4 deletions Split/FetcherEngine/Refresh/PeriodicSyncWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class BasePeriodicSyncWorker: PeriodicSyncWorker {
Logger.i("Fetch from remote not implemented")
}

func notifyUpdate(_ event: SplitInternalEvent, _ metadata: EventMetadata? = nil) {
func notifyUpdate(_ event: SplitInternalEvent, metadata: EventMetadata? = nil) {
eventsManager.notifyInternalEvent(event, metadata: metadata)
}
}
Expand Down Expand Up @@ -184,7 +184,7 @@ class PeriodicSplitsSyncWorker: BasePeriodicSyncWorker {

if result.success, result.featureFlagsUpdated.count > 0 {
let metadata = EventMetadata(type: .FLAGS_UPDATED, data: result.featureFlagsUpdated)
notifyUpdate(.splitsUpdated, metadata)
notifyUpdate(.splitsUpdated, metadata: metadata)
}
}
}
Expand Down Expand Up @@ -223,9 +223,14 @@ class PeriodicMySegmentsSyncWorker: BasePeriodicSyncWorker {
mlsTill: myLargeSegmentsStorage.changeNumber,
headers: nil)
if result.success {
if result.msUpdated || result.mlsUpdated {
if !result.msUpdated.isEmpty || !result.mlsUpdated.isEmpty {
// For now is not necessary specify which entity was updated
notifyUpdate(.mySegmentsUpdated)
if !result.msUpdated.isEmpty {
notifyUpdate(.mySegmentsUpdated, metadata: EventMetadata(type: .SEGMENTS_UPDATED, data: result.msUpdated))
}
if !result.mlsUpdated.isEmpty {
notifyUpdate(.myLargeSegmentsUpdated, metadata: EventMetadata(type: .LARGE_SEGMENTS_UPDATED, data: result.msUpdated))
}
}
}
} catch {
Expand Down
34 changes: 21 additions & 13 deletions Split/FetcherEngine/Refresh/RetryableSegmentsSyncWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@ class RetryableMySegmentsSyncWorker: BaseRetryableSyncWorker {
if result.success {
if !isSdkReadyTriggered() {
// Notifying both to trigger SDK Ready
notifyUpdate(.mySegmentsUpdated)
notifyUpdate(.myLargeSegmentsUpdated)
} else if result.msUpdated || result.mlsUpdated {
notifyUpdate(.mySegmentsUpdated, metadata: EventMetadata(type: .SEGMENTS_UPDATED, data: result.msUpdated))
notifyUpdate(.myLargeSegmentsUpdated, metadata: EventMetadata(type: .LARGE_SEGMENTS_UPDATED, data: result.mlsUpdated))
} else if !result.msUpdated.isEmpty || !result.mlsUpdated.isEmpty {
// For now is not necessary specify which entity was updated
notifyUpdate(.mySegmentsUpdated)
if !result.msUpdated.isEmpty {
notifyUpdate(.mySegmentsUpdated, metadata: EventMetadata(type: .SEGMENTS_UPDATED, data: result.msUpdated))
}
if !result.mlsUpdated.isEmpty {
notifyUpdate(.myLargeSegmentsUpdated, metadata: EventMetadata(type: .LARGE_SEGMENTS_UPDATED, data: result.mlsUpdated))
}
}
return true
}
Expand All @@ -71,8 +76,8 @@ struct SegmentsSyncResult {
let success: Bool
let msChangeNumber: Int64
let mlsChangeNumber: Int64
let msUpdated: Bool
let mlsUpdated: Bool
let msUpdated: [String]
let mlsUpdated: [String]
}

protocol SegmentsSyncHelper {
Expand All @@ -86,8 +91,8 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper {
struct FetchResult {
let msTill: Int64
let mlsTill: Int64
let msUpdated: Bool
let mlsUdated: Bool
let msUpdated: [String]
let mlsUpdated: [String]
}

private let segmentsFetcher: HttpMySegmentsFetcher
Expand Down Expand Up @@ -165,7 +170,7 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper {
msChangeNumber: result.msTill,
mlsChangeNumber: result.mlsTill,
msUpdated: result.msUpdated,
mlsUpdated: result.mlsUdated)
mlsUpdated: result.mlsUpdated)
}
attemptCount+=1
if attemptCount < maxAttempts {
Expand All @@ -175,8 +180,8 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper {
return SegmentsSyncResult(success: false,
msChangeNumber: -1,
mlsChangeNumber: -1,
msUpdated: false,
mlsUpdated: false)
msUpdated: [],
mlsUpdated: [])
}

private func fetchUntil(till: Int64?,
Expand Down Expand Up @@ -210,10 +215,13 @@ class DefaultSegmentsSyncHelper: SegmentsSyncHelper {
Logger.d("Checking my large segments update")
checkAndUpdate(isChanged: mlsChanged, change: myLargeSegmentsChange, storage: myLargeSegmentsStorage)

let segmentsDiff = changeChecker.getSegmentsDiff(oldSegments: oldChange.segments, newSegments: mySegmentsChange.segments)
let largeSegmentsDiff = changeChecker.getSegmentsDiff(oldSegments: oldLargeChange.segments, newSegments: myLargeSegmentsChange.segments)

return FetchResult(msTill: mySegmentsChange.unwrappedChangeNumber,
mlsTill: myLargeSegmentsChange.unwrappedChangeNumber,
msUpdated: msChanged,
mlsUdated: mlsChanged)
msUpdated: segmentsDiff,
mlsUpdated: largeSegmentsDiff)
}
prevChange = change
}
Expand Down
4 changes: 3 additions & 1 deletion Split/Localhost/LocalhostSynchronizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ class LocalhostSynchronizer: FeatureFlagsSynchronizer {
// Update will remove all records before insert new ones
_ = self.featureFlagsStorage.update(splitChange: change)

self.eventsManager.notifyInternalEvent(.splitsUpdated, metadata: EventMetadata(type: .FLAGS_UPDATED, data: values.map { $0.name ?? "" } ))
// Notify event
let metadata = EventMetadata(type: .FLAGS_UPDATED, data: values.map { $0.name ?? "" } )
self.eventsManager.notifyInternalEvent(.splitsUpdated, metadata: metadata)
}
}
}
9 changes: 1 addition & 8 deletions Split/Network/Streaming/PushNotificationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,7 @@ class DefaultPushNotificationManager: PushNotificationManager {
}
Logger.d("Streaming authentication success")

var connectionDelay: Int64 = 0

#if DEBUG
connectionDelay = 1
#else
connectionDelay = result.sseConnectionDelay
#endif

let connectionDelay = result.sseConnectionDelay
self.broadcasterChannel.push(event: .pushDelayReceived(delaySeconds: connectionDelay))
let lastId = lastConnId.value
if connectionDelay > 0 {
Expand Down
15 changes: 8 additions & 7 deletions Split/Network/Streaming/SyncSegmentsUpdateWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class SegmentsUpdateWorker: UpdateWorker<MembershipsUpdateNotification> {
if segments.count > newSegments.count {
mySegmentsStorage.set(SegmentChange(segments: newSegments.asArray()),
forKey: key)
synchronizer.notifyUpdate(forKey: key)
synchronizer.notifyUpdate(forKey: key, EventMetadata(type: .SEGMENTS_UPDATED, data: newSegments.asArray() ))
telemetryProducer?.recordUpdatesFromSse(type: resource)
}
}
Expand All @@ -128,7 +128,7 @@ class SegmentsUpdateWorker: UpdateWorker<MembershipsUpdateNotification> {
if oldSegments.count < newSegments.count {
mySegmentsStorage.set(SegmentChange(segments: newSegments.asArray()),
forKey: userKey)
synchronizer.notifyUpdate(forKey: userKey)
synchronizer.notifyUpdate(forKey: userKey, EventMetadata(type: .SEGMENTS_UPDATED, data: newSegments.asArray() ))
telemetryProducer?.recordUpdatesFromSse(type: .mySegments)
}
return
Expand Down Expand Up @@ -171,7 +171,7 @@ class SegmentsUpdateWorker: UpdateWorker<MembershipsUpdateNotification> {

protocol SegmentsSynchronizerWrapper {
func fetch(byKey: String, changeNumbers: SegmentsChangeNumber, delay: Int64)
func notifyUpdate(forKey: String)
func notifyUpdate(forKey: String, _ metadata: EventMetadata?)
}

class MySegmentsSynchronizerWrapper: SegmentsSynchronizerWrapper {
Expand All @@ -185,12 +185,13 @@ class MySegmentsSynchronizerWrapper: SegmentsSynchronizerWrapper {
synchronizer.forceMySegmentsSync(forKey: key, changeNumbers: changeNumbers, delay: delay)
}

func notifyUpdate(forKey key: String) {
synchronizer.notifySegmentsUpdated(forKey: key)
func notifyUpdate(forKey key: String, _ metadata: EventMetadata? = nil) {
synchronizer.notifySegmentsUpdated(forKey: key, metadata: metadata)
}
}

class MyLargeSegmentsSynchronizerWrapper: SegmentsSynchronizerWrapper {

private let synchronizer: Synchronizer

init(synchronizer: Synchronizer) {
Expand All @@ -201,8 +202,8 @@ class MyLargeSegmentsSynchronizerWrapper: SegmentsSynchronizerWrapper {
synchronizer.forceMySegmentsSync(forKey: key, changeNumbers: changeNumbers, delay: delay)
}

func notifyUpdate(forKey key: String) {
synchronizer.notifyLargeSegmentsUpdated(forKey: key)
func notifyUpdate(forKey key: String, _ metadata: EventMetadata? = nil) {
synchronizer.notifyLargeSegmentsUpdated(forKey: key, metadata: metadata)
}
}

Expand Down
13 changes: 7 additions & 6 deletions Split/Network/Streaming/SyncUpdateWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,15 @@ class SplitsUpdateWorker: UpdateWorker<TargetingRuleUpdateNotification> {

Logger.v("RBS update received: \(change)")

let processedChange = ruleBasedSegmentsChangeProcessor.process(change)
let processedSegments = ruleBasedSegmentsChangeProcessor.process(change)

if ruleBasedSegmentsStorage.update(toAdd: processedChange.toAdd,
toRemove: processedChange.toRemove,
changeNumber: processedChange.changeNumber) {
synchronizer.notifyFeatureFlagsUpdated(flags: []) //TODO: RBS Update
if ruleBasedSegmentsStorage.update(toAdd: processedSegments.toAdd,
toRemove: processedSegments.toRemove,
changeNumber: processedSegments.changeNumber) {
var updatedSegments = (processedSegments.activeSegments + processedSegments.archivedSegments).compactMap(\.name)
synchronizer.notifyFeatureFlagsUpdated(flags: []) //TODO: Make new notify segments updated (new notification method?)
}

telemetryProducer?.recordUpdatesFromSse(type: .splits)
return true
} catch {
Expand Down
12 changes: 6 additions & 6 deletions Split/Network/Sync/ByKeyFacade.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ protocol ByKeyRegistry {
protocol ByKeySynchronizer {
func loadMySegmentsFromCache(forKey: String)
func loadAttributesFromCache(forKey: String)
func notifyMySegmentsUpdated(forKey: String)
func notifyMyLargeSegmentsUpdated(forKey: String)
func notifyMySegmentsUpdated(forKey: String, metadata: EventMetadata?)
func notifyMyLargeSegmentsUpdated(forKey: String, metadata: EventMetadata?)
func startSync(forKey key: Key)
func startPeriodicSync()
func stopPeriodicSync()
Expand Down Expand Up @@ -120,15 +120,15 @@ class DefaultByKeyFacade: ByKeyFacade {
byKeyComponents.value(forKey: key)?.mySegmentsSynchronizer.synchronizeMySegments()
}

func notifyMySegmentsUpdated(forKey key: String) {
func notifyMySegmentsUpdated(forKey key: String, metadata: EventMetadata?) {
doInAll(forMatchingKey: key) { group in
group.eventsManager.notifyInternalEvent(.mySegmentsUpdated)
group.eventsManager.notifyInternalEvent(.mySegmentsUpdated, metadata: metadata)
}
}

func notifyMyLargeSegmentsUpdated(forKey key: String) {
func notifyMyLargeSegmentsUpdated(forKey key: String, metadata: EventMetadata? = nil) {
doInAll(forMatchingKey: key) { group in
group.eventsManager.notifyInternalEvent(.myLargeSegmentsUpdated)
group.eventsManager.notifyInternalEvent(.myLargeSegmentsUpdated, metadata: metadata)
}
}

Expand Down
12 changes: 6 additions & 6 deletions Split/Network/Sync/Synchronizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ protocol Synchronizer: ImpressionLogger {
func stopRecordingTelemetry()
func pushEvent(event: EventDTO)
func notifyFeatureFlagsUpdated(flags: [String])
func notifySegmentsUpdated(forKey key: String)
func notifyLargeSegmentsUpdated(forKey key: String)
func notifySegmentsUpdated(forKey key: String, metadata: EventMetadata?)
func notifyLargeSegmentsUpdated(forKey key: String, metadata: EventMetadata?)
func notifySplitKilled(flag: String)
func pause()
func resume()
Expand Down Expand Up @@ -211,12 +211,12 @@ class DefaultSynchronizer: Synchronizer {
featureFlagsSynchronizer.notifyUpdated(flagsList: flags)
}

func notifySegmentsUpdated(forKey key: String) {
byKeySynchronizer.notifyMySegmentsUpdated(forKey: key)
func notifySegmentsUpdated(forKey key: String, metadata: EventMetadata? = nil) {
byKeySynchronizer.notifyMySegmentsUpdated(forKey: key, metadata: metadata)
}

func notifyLargeSegmentsUpdated(forKey key: String) {
byKeySynchronizer.notifyMyLargeSegmentsUpdated(forKey: key)
func notifyLargeSegmentsUpdated(forKey key: String, metadata: EventMetadata? = nil) {
byKeySynchronizer.notifyMyLargeSegmentsUpdated(forKey: key, metadata: metadata)
}

func notifySplitKilled(flag: String) {
Expand Down
8 changes: 6 additions & 2 deletions SplitTests/Fake/Service/ByKeyFacadeMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,16 @@ class ByKeyFacadeMock: ByKeyFacade {
}

var notifyMySegmentsUpdatedCalled = false
func notifyMySegmentsUpdated(forKey key: String) {
var updatedSegmentsMetadataForKey = [String : EventMetadata?]()
func notifyMySegmentsUpdated(forKey key: String, metadata: EventMetadata? = nil) {
updatedSegmentsMetadataForKey[key] = metadata
notifyMySegmentsUpdatedCalled = true
}

var notifyMyLargeSegmentsUpdatedCalled = false
func notifyMyLargeSegmentsUpdated(forKey key: String) {
var updatedLargeSegmentsMetadataForKey = [String : EventMetadata?]()
func notifyMyLargeSegmentsUpdated(forKey key: String, metadata: EventMetadata? = nil) {
updatedLargeSegmentsMetadataForKey[key] = metadata
notifyMyLargeSegmentsUpdatedCalled = true
}

Expand Down
Loading
Loading