Skip to content
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

feat(ats): add configurable timeouts for ODP segment fetch and event dispatch #471

Merged
merged 12 commits into from
Dec 16, 2022
2 changes: 1 addition & 1 deletion .github/workflows/source_clear_cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
source_clear:
runs-on: macos-latest
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- name: Source clear scan
Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
TRAVIS_COM_TOKEN: ${{ secrets.TRAVIS_COM_TOKEN }}

lint:
runs-on: macos-latest
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- uses: maxim-lobanov/setup-xcode@v1
Expand All @@ -47,10 +47,12 @@ jobs:

unittests:
if: "${{ github.event.inputs.PREP == '' && github.event.inputs.RELEASE == '' }}"
uses: optimizely/swift-sdk/.github/workflows/unit_tests.yml@master
# restore back to master after merging.
#uses: optimizely/swift-sdk/.github/workflows/unit_tests.yml@master
uses: optimizely/swift-sdk/.github/workflows/unit_tests.yml@jae/ats-timeout

prepare_for_release:
runs-on: macos-latest
runs-on: macos-12
if: "${{ github.event.inputs.PREP == 'true' && github.event_name == 'workflow_dispatch' }}"
steps:
- uses: actions/checkout@v3
Expand All @@ -74,7 +76,7 @@ jobs:

release:
if: "${{github.event.inputs.RELEASE == 'true' && github.event_name == 'workflow_dispatch' }}"
runs-on: macos-latest
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- uses: maxim-lobanov/setup-xcode@v1
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ jobs:
- uses: actions/checkout@v3
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 14.1.0
# macos version and supported simulator_xcode_versions are all related to this xcode_version, so be careful when you upgrade this.
xcode-version: 12.4
- name: set SDK Branch if PR
if: ${{ github.event_name == 'pull_request' }}
run: |
Expand Down
20 changes: 16 additions & 4 deletions Sources/ODP/OdpEventApiManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ import Foundation
*/

class OdpEventApiManager {

let resourceTimeoutInSecs: Int?

/// OdpEventApiManager init
/// - Parameters:
/// - timeout: timeout for segment fetch
init(timeout: Int? = nil) {
self.resourceTimeoutInSecs = timeout
}

func sendOdpEvents(apiKey: String,
apiHost: String,
events: [OdpEvent],
Expand Down Expand Up @@ -99,8 +107,12 @@ class OdpEventApiManager {
task.resume()
}

func getSession() -> URLSession {
return URLSession(configuration: .ephemeral)
open func getSession() -> URLSession {
let config = URLSessionConfiguration.ephemeral
if let timeout = resourceTimeoutInSecs, timeout > 0 {
config.timeoutIntervalForResource = TimeInterval(timeout)
}
return URLSession(configuration: config)
}

}
13 changes: 11 additions & 2 deletions Sources/ODP/OdpEventManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,18 @@ class OdpEventManager {

let logger = OPTLoggerFactory.getLogger()

init(sdkKey: String, odpConfig: OdpConfig? = nil, apiManager: OdpEventApiManager? = nil) {
/// OdpEventManager init
/// - Parameters:
/// - sdkKey: datafile sdkKey
/// - odpConfig: ODP config (apiKey, apiHost, ...)
/// - apiManager: OdpEventApiManager
/// - resourceTimeoutInSecs: timeout for event dispatch
init(sdkKey: String,
odpConfig: OdpConfig? = nil,
apiManager: OdpEventApiManager? = nil,
resourceTimeoutInSecs: Int? = nil) {
self.odpConfig = odpConfig ?? OdpConfig()
self.apiMgr = apiManager ?? OdpEventApiManager()
self.apiMgr = apiManager ?? OdpEventApiManager(timeout: resourceTimeoutInSecs)

self.queueLock = DispatchQueue(label: "event")

Expand Down
19 changes: 17 additions & 2 deletions Sources/ODP/OdpManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,22 @@ class OdpManager {
return vuidManager.vuid
}

/// OdpManager init
/// - Parameters:
/// - sdkKey: datafile sdkKey
/// - disable: disable ODP
/// - cacheSize: segment cache size
/// - cacheTimeoutInSecs: segment cache timeout
/// - timeoutForSegmentFetchInSecs: timeout for segment fetch
/// - timeoutForEventDispatchInSecs: timeout for event dispatch
/// - segmentManager: ODPSegmentManager
/// - eventManager: ODPEventManager
init(sdkKey: String,
disable: Bool,
cacheSize: Int,
cacheTimeoutInSecs: Int,
timeoutForSegmentFetchInSecs: Int? = nil,
timeoutForEventDispatchInSecs: Int? = nil,
segmentManager: OdpSegmentManager? = nil,
eventManager: OdpEventManager? = nil) {

Expand All @@ -53,14 +65,17 @@ class OdpManager {
} else {
self.segmentManager = OdpSegmentManager(cacheSize: cacheSize,
cacheTimeoutInSecs: cacheTimeoutInSecs,
odpConfig: odpConfig)
odpConfig: odpConfig,
resourceTimeoutInSecs: timeoutForSegmentFetchInSecs)
}

if let eventManager = eventManager {
eventManager.odpConfig = odpConfig
self.eventManager = eventManager
} else {
self.eventManager = OdpEventManager(sdkKey: sdkKey, odpConfig: odpConfig)
self.eventManager = OdpEventManager(sdkKey: sdkKey,
odpConfig: odpConfig,
resourceTimeoutInSecs: timeoutForEventDispatchInSecs)
}

self.eventManager.registerVUID(vuid: self.vuidManager.vuid)
Expand Down
16 changes: 14 additions & 2 deletions Sources/ODP/OdpSegmentApiManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ import Foundation

class OdpSegmentApiManager {
let logger = OPTLoggerFactory.getLogger()
let resourceTimeoutInSecs: Int?

/// OdpSegmentApiManager init
/// - Parameters:
/// - timeout: timeout for segment fetch
init(timeout: Int? = nil) {
self.resourceTimeoutInSecs = timeout
}

func fetchSegments(apiKey: String,
apiHost: String,
Expand Down Expand Up @@ -175,8 +183,12 @@ class OdpSegmentApiManager {
task.resume()
}

func getSession() -> URLSession {
return URLSession(configuration: .ephemeral)
open func getSession() -> URLSession {
let config = URLSessionConfiguration.ephemeral
if let timeout = resourceTimeoutInSecs, timeout > 0 {
config.timeoutIntervalForResource = TimeInterval(timeout)
}
return URLSession(configuration: config)
}

func makeQuery(userKey: String, userValue: String, segmentsToCheck: [String]) -> [String: Any] {
Expand Down
12 changes: 10 additions & 2 deletions Sources/ODP/OdpSegmentManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ class OdpSegmentManager {

let logger = OPTLoggerFactory.getLogger()

/// OdpSegmentManager init
/// - Parameters:
/// - cacheSize: segment cache size
/// - cacheTimeoutInSecs: segment cache timeout
/// - odpConfig: ODP config (apiKey, apiHost, ...)
/// - apiManager: OdpSegmentApiManager
/// - resourceTimeoutInSecs: timeout for segment fetch
init(cacheSize: Int,
cacheTimeoutInSecs: Int,
odpConfig: OdpConfig? = nil,
apiManager: OdpSegmentApiManager? = nil) {
apiManager: OdpSegmentApiManager? = nil,
resourceTimeoutInSecs: Int? = nil) {
self.odpConfig = odpConfig ?? OdpConfig()
self.apiMgr = apiManager ?? OdpSegmentApiManager()
self.apiMgr = apiManager ?? OdpSegmentApiManager(timeout: resourceTimeoutInSecs)

self.segmentsCache = LruCache<String, [String]>(size: cacheSize,
timeoutInSecs: cacheTimeoutInSecs)
Expand Down
10 changes: 10 additions & 0 deletions Sources/ODP/OptimizelySdkSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public struct OptimizelySdkSettings {
let segmentsCacheSize: Int
/// The timeout in seconds of audience segments cache - timeout is disabled if this is set to zero.
let segmentsCacheTimeoutInSecs: Int
/// The timeout in seconds of odp segment fetch - OS default timeout will be used if this is set to zero.
let timeoutForSegmentFetchInSecs: Int
/// The timeout in seconds of odp event dispatch - OS default timeout will be used if this is set to zero.
let timeoutForOdpEventInSecs: Int
/// ODP features are disabled if this is set to true.
let disableOdp: Bool

Expand All @@ -29,12 +33,18 @@ public struct OptimizelySdkSettings {
/// - Parameters:
/// - segmentsCacheSize: The maximum size of audience segments cache (optional. default = 100). Set to zero to disable caching.
/// - segmentsCacheTimeoutInSecs: The timeout in seconds of audience segments cache (optional. default = 600). Set to zero to disable timeout.
/// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero.
/// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero.
/// - disableOdp: Set this flag to true (default = false) to disable ODP features
public init(segmentsCacheSize: Int = 100,
segmentsCacheTimeoutInSecs: Int = 600,
timeoutForSegmentFetchInSecs: Int = 10,
timeoutForOdpEventInSecs: Int = 10,
disableOdp: Bool = false) {
self.segmentsCacheSize = segmentsCacheSize
self.segmentsCacheTimeoutInSecs = segmentsCacheTimeoutInSecs
self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs
self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs
self.disableOdp = disableOdp
}
}
4 changes: 3 additions & 1 deletion Sources/Optimizely/OptimizelyClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ open class OptimizelyClient: NSObject {
self.odpManager = OdpManager(sdkKey: sdkKey,
disable: sdkSettings.disableOdp,
cacheSize: sdkSettings.segmentsCacheSize,
cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs)
cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs,
timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs,
timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs)

super.init()

Expand Down
15 changes: 12 additions & 3 deletions Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,29 @@ class OptimizelyClientTests_ODP: XCTestCase {

// MARK: - ODP configuration

func testConfigurableSettings_default() {
func testSdkSettings_default() {
let optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey)

XCTAssertEqual(100, optimizely.odpManager.segmentManager?.segmentsCache.maxSize)
XCTAssertEqual(600, optimizely.odpManager.segmentManager?.segmentsCache.timeoutInSecs)
XCTAssertEqual(10, optimizely.odpManager.segmentManager?.apiMgr.resourceTimeoutInSecs)
XCTAssertEqual(10, optimizely.odpManager.eventManager?.apiMgr.resourceTimeoutInSecs)
XCTAssertEqual(true, optimizely.odpManager.enabled)
}

func testConfigurableSettings_custom() {
var sdkSettings = OptimizelySdkSettings(segmentsCacheSize: 12, segmentsCacheTimeoutInSecs: 345)
func testSdkSettings_custom() {
var sdkSettings = OptimizelySdkSettings(segmentsCacheSize: 12,
segmentsCacheTimeoutInSecs: 345)
var optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings)
XCTAssertEqual(12, optimizely.odpManager.segmentManager?.segmentsCache.maxSize)
XCTAssertEqual(345, optimizely.odpManager.segmentManager?.segmentsCache.timeoutInSecs)

sdkSettings = OptimizelySdkSettings(timeoutForSegmentFetchInSecs: 34,
timeoutForOdpEventInSecs: 45)
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings)
XCTAssertEqual(34, optimizely.odpManager.segmentManager?.apiMgr.resourceTimeoutInSecs)
XCTAssertEqual(45, optimizely.odpManager.eventManager?.apiMgr.resourceTimeoutInSecs)

sdkSettings = OptimizelySdkSettings(disableOdp: true)
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings)
XCTAssertEqual(false, optimizely.odpManager.enabled)
Expand Down
12 changes: 12 additions & 0 deletions Tests/OptimizelyTests-Common/OdpEventApiManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ class OdpEventApiManagerTests: XCTestCase {
XCTAssert(jsonDispatched.contains("\"key3\":null"))
}

// MARK: - timeout

func testTimeout() {
let api = OdpEventApiManager(timeout: 3)
XCTAssertEqual(3, api.getSession().configuration.timeoutIntervalForResource)
}

func testTimeout_useOSDefaultIfTimeoutIsNotProvided() {
let api = OdpEventApiManager()
XCTAssertEqual(604800, api.getSession().configuration.timeoutIntervalForResource)
}

}

// MARK: - Tests with live ODP server
Expand Down
1 change: 0 additions & 1 deletion Tests/OptimizelyTests-Common/OdpManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class OdpManagerTests: XCTestCase {
disable: true,
cacheSize: cacheSize,
cacheTimeoutInSecs: cacheTimeout)

XCTAssertTrue(manager.vuid.starts(with: "vuid_"), "vuid should be serverved even when ODP is disabled.")

let sem = DispatchSemaphore(value: 0)
Expand Down
12 changes: 12 additions & 0 deletions Tests/OptimizelyTests-Common/OdpSegmentApiManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,18 @@ class OdpSegmentApiManagerTests: XCTestCase {
XCTAssertEqual(.success, sem.wait(timeout: .now() + .seconds(1)))
}

// MARK: - timeout

func testTimeout() {
let api = OdpSegmentApiManager(timeout: 3)
XCTAssertEqual(3, api.getSession().configuration.timeoutIntervalForResource)
}

func testTimeout_useOSDefaultIfTimeoutIsNotProvided() {
let api = OdpSegmentApiManager()
XCTAssertEqual(604800, api.getSession().configuration.timeoutIntervalForResource)
}

// MARK: - Others

func testMakeQuery() {
Expand Down