@@ -35,13 +35,35 @@ class DefaultCmabService
3535 #
3636 # @raise [ArgumentError] If cmab_cache is not an instance of LRUCache.
3737 # @raise [ArgumentError] If cmab_client is not an instance of DefaultCmabClient.
38+
39+ NUM_LOCK_STRIPES = 1000
40+
3841 def initialize ( cmab_cache , cmab_client , logger = nil )
3942 @cmab_cache = cmab_cache
4043 @cmab_client = cmab_client
4144 @logger = logger
45+ @locks = Array . new ( NUM_LOCK_STRIPES ) { Mutex . new }
4246 end
4347
4448 def get_decision ( project_config , user_context , rule_id , options )
49+ lock_index = get_lock_index ( user_context . user_id , rule_id )
50+
51+ @locks [ lock_index ] . synchronize do
52+ get_decision_impl ( project_config , user_context , rule_id , options )
53+ end
54+ end
55+
56+ private
57+
58+ def get_lock_index ( user_id , rule_id )
59+ # Create a hash of user_id + rule_id for consistent lock selection
60+ hash_input = "#{ user_id } #{ rule_id } "
61+ # Use Ruby's built-in hash method and ensure positive result
62+ hash_value = hash_input . hash . abs
63+ hash_value % NUM_LOCK_STRIPES
64+ end
65+
66+ def get_decision_impl ( project_config , user_context , rule_id , options )
4567 # Retrieves a decision for a given user and rule, utilizing a cache for efficiency.
4668 #
4769 # This method filters user attributes, checks for various cache-related options,
@@ -85,8 +107,6 @@ def get_decision(project_config, user_context, rule_id, options)
85107 cmab_decision
86108 end
87109
88- private
89-
90110 def fetch_decision ( rule_id , user_id , attributes )
91111 # Fetches a decision for a given rule and user, along with user attributes.
92112 #
0 commit comments