@@ -48,25 +48,42 @@ class DefaultCmabService: CmabService {
4848 private let cmabCache : LruCache < String , CmabCacheValue >
4949 private let logger = OPTLoggerFactory . getLogger ( )
5050
51+ private static let NUM_LOCKS = 1000
52+ private let locks : [ NSLock ]
53+
5154 init ( cmabClient: CmabClient , cmabCache: LruCache < String , CmabCacheValue > ) {
5255 self . cmabClient = cmabClient
5356 self . cmabCache = cmabCache
57+ self . locks = ( 0 ..< Self . NUM_LOCKS) . map { _ in NSLock ( ) }
58+ }
59+
60+ private func getLockIndex( userId: String , ruleId: String ) -> Int {
61+ let combinedKey = userId + ruleId
62+ let hashValue = combinedKey. hashValue
63+ // Take absolute value to ensure positive number
64+ let positiveHash = abs ( hashValue)
65+ // Use modulo to map to lock array index [0, NUM_LOCKS-1]
66+ return positiveHash % Self. NUM_LOCKS
5467 }
5568
5669 func getDecision( config: ProjectConfig ,
5770 userContext: OptimizelyUserContext ,
5871 ruleId: String ,
5972 options: [ OptimizelyDecideOption ] ) -> Result < CmabDecision , Error > {
60- var result : Result < CmabDecision , Error > !
61- let semaphore = DispatchSemaphore ( value: 0 )
62- getDecision ( config: config,
63- userContext: userContext,
64- ruleId: ruleId, options: options) { _result in
65- result = _result
66- semaphore. signal ( )
73+ let lockIdx = getLockIndex ( userId: userContext. userId, ruleId: ruleId)
74+ let lock = locks [ lockIdx]
75+ return lock. withLock {
76+ var result : Result < CmabDecision , Error > !
77+ let semaphore = DispatchSemaphore ( value: 0 )
78+ getDecision ( config: config,
79+ userContext: userContext,
80+ ruleId: ruleId, options: options) { _result in
81+ result = _result
82+ semaphore. signal ( )
83+ }
84+ semaphore. wait ( )
85+ return result
6786 }
68- semaphore. wait ( )
69- return result
7087 }
7188
7289 func getDecision( config: ProjectConfig ,
0 commit comments