@@ -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