diff --git a/CHANGES.txt b/CHANGES.txt index 9dda1b5c..6893f58c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,8 @@ CHANGES +8.1.0 (Oct 4, 2022) +- Added a new impressions mode for the SDK called NONE , to be used in factory when there is no desire to capture impressions on an SDK factory to feed Split's analytics engine. Running NONE mode, the SDK will only capture unique keys evaluated for a particular feature flag instead of full blown impressions. + 8.0.1 (Jul 20, 2022) - Updated dependencies to support faraday > 2.0 @@ -49,9 +52,10 @@ CHANGES - Updated ably error handling. 7.2.0 (Sep 25, 2020) -- Added deduplication logic for impressions data. - - Now there are two modes for Impressions when the SDK is in standalone mode, OPTIMIZED (default) that only ships unique impressions and DEBUG for times where you need to send ALL impressions to debug an integration. - - Impression listener remains unchanged and will still get all impressions. +- Added impressions dedupe logic to avoid sending duplicated impressions: + - Added `OPTIMIZED` and `DEBUG` modes in order to enabling/disabling how impressions are going to be sent into Split servers, + - `OPTIMIZED`: will send unique impressions in a timeframe in order to reduce how many times impressions are posted to Split. + - `DEBUG`: will send every impression generated to Split. 7.1.3 (Jul 31, 2020) - Updated rake development dependency to ~> 12.3.3. diff --git a/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb b/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb index 53f1593f..8b7b86ac 100644 --- a/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb +++ b/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb @@ -12,7 +12,9 @@ def initialize(config) end def record_uniques_key(uniques) - formatted = uniques_formatter(uniques) + return if uniques.nil? || uniques == {} + + formatted = uniques_formatter(uniques).to_json unless formatted.nil? size = @adapter.add_to_queue(unique_keys_key, formatted) diff --git a/lib/splitclient-rb/engine/common/impressions_manager.rb b/lib/splitclient-rb/engine/common/impressions_manager.rb index 1cb4cda3..a4cfb208 100644 --- a/lib/splitclient-rb/engine/common/impressions_manager.rb +++ b/lib/splitclient-rb/engine/common/impressions_manager.rb @@ -30,7 +30,8 @@ def build_impression(matching_key, bucketing_key, split_name, treatment, params @unique_keys_tracker.track(split_name, matching_key) else # In OPTIMIZED mode we should track the total amount of evaluations and deduplicate the impressions. impression_data[:pt] = @impression_observer.test_and_set(impression_data) - @impression_counter.inc(split_name, impression_data[:m]) + + @impression_counter.inc(split_name, impression_data[:m]) unless impression_data[:pt].nil? end rescue StandardError => e @config.log_found_exception(__method__.to_s, e) diff --git a/lib/splitclient-rb/split_config.rb b/lib/splitclient-rb/split_config.rb index 22508cbd..486f64ab 100644 --- a/lib/splitclient-rb/split_config.rb +++ b/lib/splitclient-rb/split_config.rb @@ -310,23 +310,18 @@ def self.default_on_demand_fetch_max_retries 10 end - def self.default_impressions_mode - :optimized - end - def init_impressions_mode(impressions_mode, adapter) - impressions_mode ||= SplitConfig.default_impressions_mode - - return :debug if adapter == :redis - case impressions_mode + when :optimized + return :optimized + when :none + return :none when :debug return :debug - # when :none // we not support :none impression mode yet. Defaulting to :optimized mode - # return :none else - @logger.error('You passed an invalid impressions_mode, impressions_mode should be one of the following values: :debug or :optimized. Defaulting to :optimized mode') unless impressions_mode == :optimized - return :optimized + default = adapter == :redis ? :debug : :optimized + @logger.error("You passed an invalid impressions_mode, impressions_mode should be one of the following values: :debug, :optimized or :none. Defaulting to #{default} mode") + return default end end diff --git a/lib/splitclient-rb/version.rb b/lib/splitclient-rb/version.rb index 516fd124..88b820b4 100644 --- a/lib/splitclient-rb/version.rb +++ b/lib/splitclient-rb/version.rb @@ -1,3 +1,3 @@ module SplitIoClient - VERSION = '8.0.1' + VERSION = '8.1.0' end diff --git a/spec/cache/senders/impressions_sender_adapter_spec.rb b/spec/cache/senders/impressions_sender_adapter_spec.rb index 5b7c9c11..03e99eca 100644 --- a/spec/cache/senders/impressions_sender_adapter_spec.rb +++ b/spec/cache/senders/impressions_sender_adapter_spec.rb @@ -22,10 +22,18 @@ sender.record_uniques_key(uniques) result = config.cache_adapter.get_from_queue(unique_keys_key, 0) - expect(result.size).to eq(3) - expect(result[0]).to eq('{:f=>"feature-name-1", :k=>["key-1", "key-2", "key-3", "key-4"]}') - expect(result[1]).to eq('{:f=>"feature-name-2", :k=>["key-1", "key-2", "key-3", "key-4"]}') - expect(result[2]).to eq('{:f=>"feature-name-3", :k=>["key-1", "key-2", "key-3", "key-4"]}') + + expect(result.size).to eq(1) + data = JSON.parse(result[0], symbolize_names: true) + + expect(data[0][:f]).to eq('feature-name-1') + expect(data[0][:k].to_s).to eq('["key-1", "key-2", "key-3", "key-4"]') + + expect(data[1][:f]).to eq('feature-name-2') + expect(data[1][:k].to_s).to eq('["key-1", "key-2", "key-3", "key-4"]') + + expect(data[2][:f]).to eq('feature-name-3') + expect(data[2][:k].to_s).to eq('["key-1", "key-2", "key-3", "key-4"]') end it 'record_uniques_key when uniques is nil or empty' do diff --git a/spec/engine/impressions/redis_unique_keys_tracker_spec.rb b/spec/engine/impressions/redis_unique_keys_tracker_spec.rb index da307241..06e81d22 100644 --- a/spec/engine/impressions/redis_unique_keys_tracker_spec.rb +++ b/spec/engine/impressions/redis_unique_keys_tracker_spec.rb @@ -35,7 +35,13 @@ expect(tracker.track("feature-test-#{i}", 'key_test-2')).to eq(true) end - expect(config.cache_adapter.get_from_queue(key, 0).size).to eq(20) + result = config.cache_adapter.get_from_queue(key, 0) + expect(result.size).to eq(10) + + 10.times do |i| + data = JSON.parse(result[i], symbolize_names: true) + expect(data.size).to eq(2) + end cache.clear end @@ -56,7 +62,13 @@ sleep 1 - expect(config.cache_adapter.get_from_queue(key, 0).size).to eq(10) + result = config.cache_adapter.get_from_queue(key, 0) + expect(result.size).to eq(1) + + 10.times do |i| + data = JSON.parse(result[0], symbolize_names: true) + expect(data.size).to eq(10) + end cache.clear end diff --git a/spec/integrations/dedupe_impression_spec.rb b/spec/integrations/dedupe_impression_spec.rb index dfa61715..312b7c6d 100644 --- a/spec/integrations/dedupe_impression_spec.rb +++ b/spec/integrations/dedupe_impression_spec.rb @@ -98,8 +98,8 @@ .with( body: { pf: [ - { f: 'FACUNDO_TEST', m: time_frame, rc: 3 }, - { f: 'Test_Save_1', m: time_frame, rc: 2 } + { f: 'FACUNDO_TEST', m: time_frame, rc: 1 }, + { f: 'Test_Save_1', m: time_frame, rc: 1 } ] }.to_json )).to have_been_made @@ -120,6 +120,7 @@ client.get_treatments('admin', %w[FACUNDO_TEST MAURO_TEST Test_Save_1]) client.get_treatments('maldo', %w[FACUNDO_TEST Test_Save_1]) client.get_treatments('nico_test', %w[FACUNDO_TEST MAURO_TEST Test_Save_1]) + client.get_treatments('nico_test', %w[FACUNDO_TEST MAURO_TEST Test_Save_1]) time_frame = SplitIoClient::Engine::Common::ImpressionCounter.truncate_time_frame((Time.now.to_f * 1000.0).to_i) @@ -130,9 +131,9 @@ .with( body: { pf: [ - { f: 'FACUNDO_TEST', m: time_frame, rc: 4 }, - { f: 'MAURO_TEST', m: time_frame, rc: 3 }, - { f: 'Test_Save_1', m: time_frame, rc: 4 } + { f: 'FACUNDO_TEST', m: time_frame, rc: 2 }, + { f: 'MAURO_TEST', m: time_frame, rc: 2 }, + { f: 'Test_Save_1', m: time_frame, rc: 2 } ] }.to_json )).to have_been_made diff --git a/spec/splitclient/split_config_spec.rb b/spec/splitclient/split_config_spec.rb index 8c5542fa..c1f2f758 100644 --- a/spec/splitclient/split_config_spec.rb +++ b/spec/splitclient/split_config_spec.rb @@ -113,6 +113,16 @@ configs3 = SplitIoClient::SplitConfig.new(options3) expect(configs3.impressions_mode).to eq(:optimized) + + options4 = { impressions_mode: :sarasa, cache_adapter: :redis } + configs4 = SplitIoClient::SplitConfig.new(options4) + + expect(configs4.impressions_mode).to eq(:debug) + + options5 = { impressions_mode: :optimized, cache_adapter: :redis } + configs5 = SplitIoClient::SplitConfig.new(options5) + + expect(configs5.impressions_mode).to eq(:optimized) end it 'set impressions refresh rate with impressions optimized mode' do