1717#
1818require 'optimizely/odp/lru_cache'
1919require 'optimizely/decide/optimizely_decide_option'
20+ require 'optimizely/logger'
2021require 'digest'
2122require 'json'
2223require 'securerandom'
@@ -26,6 +27,12 @@ module Optimizely
2627 CmabDecision = Struct . new ( :variation_id , :cmab_uuid , keyword_init : true )
2728 CmabCacheValue = Struct . new ( :attributes_hash , :variation_id , :cmab_uuid , keyword_init : true )
2829
30+ class DefaultCmabCacheOptions
31+ # CMAB Constants
32+ DEFAULT_CMAB_CACHE_TIMEOUT = ( 30 * 60 ) # in seconds
33+ DEFAULT_CMAB_CACHE_SIZE = 1000
34+ end
35+
2936 # Default CMAB service implementation
3037 class DefaultCmabService
3138 # Initializes a new instance of the CmabService.
@@ -42,7 +49,7 @@ class DefaultCmabService
4249 def initialize ( cmab_cache , cmab_client , logger = nil )
4350 @cmab_cache = cmab_cache
4451 @cmab_client = cmab_client
45- @logger = logger
52+ @logger = logger || NoOpLogger . new
4653 @locks = Array . new ( NUM_LOCK_STRIPES ) { Mutex . new }
4754 end
4855
@@ -81,30 +88,65 @@ def get_decision_impl(project_config, user_context, rule_id, options)
8188 # @return [CmabDecision] The decision object containing variation_id and cmab_uuid.
8289
8390 filtered_attributes = filter_attributes ( project_config , user_context , rule_id )
91+ reasons = [ ]
92+
93+ if options &.include? ( Decide ::OptimizelyDecideOption ::IGNORE_CMAB_CACHE )
94+ reason = "Ignoring CMAB cache for user '#{ user_context . user_id } ' and rule '#{ rule_id } '"
95+ @logger . log ( Logger ::DEBUG , reason )
96+ reasons << reason
97+ cmab_decision = fetch_decision ( rule_id , user_context . user_id , filtered_attributes )
98+ return [ cmab_decision , reasons ]
99+ end
84100
85- return fetch_decision ( rule_id , user_context . user_id , filtered_attributes ) if options &.include? ( Decide ::OptimizelyDecideOption ::IGNORE_CMAB_CACHE )
86-
87- @cmab_cache . reset if options &.include? ( Decide ::OptimizelyDecideOption ::RESET_CMAB_CACHE )
101+ if options &.include? ( Decide ::OptimizelyDecideOption ::RESET_CMAB_CACHE )
102+ reason = "Resetting CMAB cache for user '#{ user_context . user_id } ' and rule '#{ rule_id } '"
103+ @logger . log ( Logger ::DEBUG , reason )
104+ reasons << reason
105+ @cmab_cache . reset
106+ end
88107
89108 cache_key = get_cache_key ( user_context . user_id , rule_id )
90109
91- @cmab_cache . remove ( cache_key ) if options &.include? ( Decide ::OptimizelyDecideOption ::INVALIDATE_USER_CMAB_CACHE )
110+ if options &.include? ( Decide ::OptimizelyDecideOption ::INVALIDATE_USER_CMAB_CACHE )
111+ reason = "Invalidating CMAB cache for user '#{ user_context . user_id } ' and rule '#{ rule_id } '"
112+ @logger . log ( Logger ::DEBUG , reason )
113+ reasons << reason
114+ @cmab_cache . remove ( cache_key )
115+ end
116+
92117 cached_value = @cmab_cache . lookup ( cache_key )
93118 attributes_hash = hash_attributes ( filtered_attributes )
94119
95120 if cached_value
96- return CmabDecision . new ( variation_id : cached_value . variation_id , cmab_uuid : cached_value . cmab_uuid ) if cached_value . attributes_hash == attributes_hash
97-
98- @cmab_cache . remove ( cache_key )
121+ if cached_value . attributes_hash == attributes_hash
122+ reason = "CMAB cache hit for user '#{ user_context . user_id } ' and rule '#{ rule_id } '"
123+ @logger . log ( Logger ::DEBUG , reason )
124+ reasons << reason
125+ return [ CmabDecision . new ( variation_id : cached_value . variation_id , cmab_uuid : cached_value . cmab_uuid ) , reasons ]
126+ else
127+ reason = "CMAB cache attributes mismatch for user '#{ user_context . user_id } ' and rule '#{ rule_id } ', fetching new decision."
128+ @logger . log ( Logger ::DEBUG , reason )
129+ reasons << reason
130+ @cmab_cache . remove ( cache_key )
131+ end
132+ else
133+ reason = "CMAB cache miss for user '#{ user_context . user_id } ' and rule '#{ rule_id } '"
134+ @logger . log ( Logger ::DEBUG , reason )
135+ reasons << reason
99136 end
137+
100138 cmab_decision = fetch_decision ( rule_id , user_context . user_id , filtered_attributes )
139+ reason = "CMAB decision is #{ cmab_decision . to_h } "
140+ @logger . log ( Logger ::DEBUG , reason )
141+ reasons << reason
142+
101143 @cmab_cache . save ( cache_key ,
102144 CmabCacheValue . new (
103145 attributes_hash : attributes_hash ,
104146 variation_id : cmab_decision . variation_id ,
105147 cmab_uuid : cmab_decision . cmab_uuid
106148 ) )
107- cmab_decision
149+ [ cmab_decision , reasons ]
108150 end
109151
110152 def fetch_decision ( rule_id , user_id , attributes )
0 commit comments