From 799ab5d4d6512405089590c55fd45c44ca88d1a7 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Tue, 21 Oct 2025 21:43:50 +0600 Subject: [PATCH 1/2] refactor: improve thread safety in getDecision function - Introduce a lock array to prevent race conditions - Implement getLockIndex method for calculating lock index based on user and rule IDs --- Sources/CMAB/CmabService.swift | 35 +++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/Sources/CMAB/CmabService.swift b/Sources/CMAB/CmabService.swift index c74205e8..be056cad 100644 --- a/Sources/CMAB/CmabService.swift +++ b/Sources/CMAB/CmabService.swift @@ -48,25 +48,42 @@ class DefaultCmabService: CmabService { private let cmabCache: LruCache private let logger = OPTLoggerFactory.getLogger() + private static let NUM_LOCKS = 1000 + private let locks: [NSLock] + init(cmabClient: CmabClient, cmabCache: LruCache) { self.cmabClient = cmabClient self.cmabCache = cmabCache + self.locks = (0.. Int { + let combinedKey = userId + ruleId + let hashValue = combinedKey.hashValue + // Take absolute value to ensure positive number + let positiveHash = abs(hashValue) + // Use modulo to map to lock array index [0, NUM_LOCKS-1] + return positiveHash % Self.NUM_LOCKS } func getDecision(config: ProjectConfig, userContext: OptimizelyUserContext, ruleId: String, options: [OptimizelyDecideOption]) -> Result { - var result: Result! - let semaphore = DispatchSemaphore(value: 0) - getDecision(config: config, - userContext: userContext, - ruleId: ruleId, options: options) { _result in - result = _result - semaphore.signal() + let lockIdx = getLockIndex(userId: userContext.userId, ruleId: ruleId) + let lock = locks[lockIdx] + return lock.withLock { + var result: Result! + let semaphore = DispatchSemaphore(value: 0) + getDecision(config: config, + userContext: userContext, + ruleId: ruleId, options: options) { _result in + result = _result + semaphore.signal() + } + semaphore.wait() + return result } - semaphore.wait() - return result } func getDecision(config: ProjectConfig, From a10e9020b07ee2ef8c4519fca4982f64feba9019 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Wed, 22 Oct 2025 19:15:53 +0600 Subject: [PATCH 2/2] refactor: update getLockIndex algorithm - Replace hash function with MurmurHash3 for improved performance - Calculate lockIndex directly instead of using intermediate variables --- Sources/CMAB/CmabService.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Sources/CMAB/CmabService.swift b/Sources/CMAB/CmabService.swift index be056cad..20c33f82 100644 --- a/Sources/CMAB/CmabService.swift +++ b/Sources/CMAB/CmabService.swift @@ -59,11 +59,9 @@ class DefaultCmabService: CmabService { private func getLockIndex(userId: String, ruleId: String) -> Int { let combinedKey = userId + ruleId - let hashValue = combinedKey.hashValue - // Take absolute value to ensure positive number - let positiveHash = abs(hashValue) - // Use modulo to map to lock array index [0, NUM_LOCKS-1] - return positiveHash % Self.NUM_LOCKS + let hashValue = MurmurHash3.hash32(key: combinedKey) + let lockIndex = Int(hashValue) % Self.NUM_LOCKS + return lockIndex } func getDecision(config: ProjectConfig,