Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update client class and fixed tests #488

Merged
merged 3 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/splitclient-rb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
require 'splitclient-rb/cache/fetchers/split_fetcher'
require 'splitclient-rb/cache/filter/bloom_filter'
require 'splitclient-rb/cache/filter/filter_adapter'
require 'splitclient-rb/cache/filter/flag_set_filter'
require 'splitclient-rb/cache/hashers/impression_hasher'
require 'splitclient-rb/cache/observers/impression_observer'
require 'splitclient-rb/cache/observers/noop_impression_observer'
Expand All @@ -24,6 +25,7 @@
require 'splitclient-rb/cache/repositories/impressions_repository'
require 'splitclient-rb/cache/repositories/events/memory_repository'
require 'splitclient-rb/cache/repositories/events/redis_repository'
require 'splitclient-rb/cache/repositories/flag_sets_repository'
require 'splitclient-rb/cache/repositories/impressions/memory_repository'
require 'splitclient-rb/cache/repositories/impressions/redis_repository'
require 'splitclient-rb/cache/senders/impressions_formatter'
Expand All @@ -43,6 +45,7 @@
require 'splitclient-rb/helpers/thread_helper'
require 'splitclient-rb/helpers/decryption_helper'
require 'splitclient-rb/helpers/util'
require 'splitclient-rb/helpers/repository_helper'
require 'splitclient-rb/split_factory'
require 'splitclient-rb/split_factory_builder'
require 'splitclient-rb/split_config'
Expand Down
31 changes: 2 additions & 29 deletions lib/splitclient-rb/cache/fetchers/split_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,16 @@ def call
fetch_splits
return
end

splits_thread
end

def fetch_splits(fetch_options = { cache_control_headers: false, till: nil })
@semaphore.synchronize do
data = splits_since(@splits_repository.get_change_number, fetch_options)

data[:splits] && data[:splits].each do |split|
add_split_unless_archived(split)
end

SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@splits_repository, data[:splits], data[:till], @config)
@splits_repository.set_segment_names(data[:segment_names])
@splits_repository.set_change_number(data[:till])

@config.logger.debug("segments seen(#{data[:segment_names].length}): #{data[:segment_names].to_a}") if @config.debug_enabled

{ segment_names: data[:segment_names], success: true }
Expand Down Expand Up @@ -64,28 +59,6 @@ def splits_since(since, fetch_options = { cache_control_headers: false, till: ni
splits_api.since(since, fetch_options)
end

def add_split_unless_archived(split)
if Engine::Models::Split.archived?(split)
@config.logger.debug("Seeing archived feature flag #{split[:name]}") if @config.debug_enabled

remove_archived_split(split)
else
store_split(split)
end
end

def remove_archived_split(split)
@config.logger.debug("removing feature flag from store(#{split})") if @config.debug_enabled

@splits_repository.remove_split(split)
end

def store_split(split)
@config.logger.debug("storing feature flag (#{split[:name]})") if @config.debug_enabled

@splits_repository.add_split(split)
end

def splits_api
@splits_api ||= SplitIoClient::Api::Splits.new(@api_key, @config, @telemetry_runtime_producer)
end
Expand Down
40 changes: 40 additions & 0 deletions lib/splitclient-rb/cache/filter/flag_set_filter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

require 'set'

module SplitIoClient
module Cache
module Filter
class FlagSetsFilter
def initialize(flag_sets = [])
@flag_sets = Set.new(flag_sets)
@should_filter = @flag_sets.any?
end

def should_filter?
@should_filter
end

def flag_set_exist?(flag_set)
return true unless @should_filter

if not flag_set.is_a?(String) or flag_set.empty?
return false
end

@flag_sets.intersection([flag_set]).any?
end

def intersect?(flag_sets)
return true unless @should_filter

if not flag_sets.is_a?(Array) or flag_sets.empty?
return false
end

@flag_sets.intersection(Set.new(flag_sets)).any?
end
end
end
end
end
38 changes: 38 additions & 0 deletions lib/splitclient-rb/cache/repositories/flag_sets_repository.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'concurrent'

module SplitIoClient
module Cache
module Repositories
class FlagSetsRepository
def initialize(flag_sets = [])
@sets_feature_flag_map = {}
flag_sets.each{ |flag_set| @sets_feature_flag_map[flag_set] = Set[] }
end

def flag_set_exist?(flag_set)
@sets_feature_flag_map.key?(flag_set)
end

def get_flag_set(flag_set)
@sets_feature_flag_map[flag_set]
end

def add_flag_set(flag_set)
@sets_feature_flag_map[flag_set] = Set[] if !flag_set_exist?(flag_set)
end

def remove_flag_set(flag_set)
@sets_feature_flag_map.delete(flag_set) if flag_set_exist?(flag_set)
end

def add_feature_flag_to_flag_set(flag_set, feature_flag)
@sets_feature_flag_map[flag_set].add(feature_flag) if flag_set_exist?(flag_set)
end

def remove_feature_flag_from_flag_set(flag_set, feature_flag)
@sets_feature_flag_map[flag_set].delete(feature_flag) if flag_set_exist?(flag_set)
end
end
end
end
end
130 changes: 92 additions & 38 deletions lib/splitclient-rb/cache/repositories/splits_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Repositories
class SplitsRepository < Repository
attr_reader :adapter

def initialize(config)
def initialize(config, flag_sets_repository, flag_set_filter)
super(config)
@tt_cache = {}
@adapter = case @config.cache_adapter.class.to_s
Expand All @@ -15,48 +15,18 @@ def initialize(config)
else
@config.cache_adapter
end
@flag_sets = flag_sets_repository
@flag_set_filter = flag_set_filter
unless @config.mode.equal?(:consumer)
@adapter.set_string(namespace_key('.splits.till'), '-1')
@adapter.initialize_map(namespace_key('.segments.registered'))
end
end

def add_split(split)
return unless split[:name]
existing_split = get_split(split[:name])

if(!existing_split)
increase_tt_name_count(split[:trafficTypeName])
elsif(existing_split[:trafficTypeName] != split[:trafficTypeName])
increase_tt_name_count(split[:trafficTypeName])
decrease_tt_name_count(existing_split[:trafficTypeName])
end

@adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
end

def remove_split(split)
tt_name = split[:trafficTypeName]

decrease_tt_name_count(split[:trafficTypeName])

@adapter.delete(namespace_key(".split.#{split[:name]}"))
end

def get_splits(names, symbolize_names = true)
splits = {}
split_names = names.map { |name| namespace_key(".split.#{name}") }
splits.merge!(
@adapter
.multiple_strings(split_names)
.map { |name, data| [name.gsub(namespace_key('.split.'), ''), data] }.to_h
)

splits.map do |name, data|
parsed_data = data ? JSON.parse(data, symbolize_names: true) : nil
split_name = symbolize_names ? name.to_sym : name
[split_name, parsed_data]
end.to_h
def update(to_add, to_delete, new_change_number)
to_add.each{ |feature_flag| add_feature_flag(feature_flag) }
to_delete.each{ |feature_flag| remove_feature_flag(feature_flag) }
set_change_number(new_change_number)
end

def get_split(name)
Expand All @@ -65,7 +35,7 @@ def get_split(name)
JSON.parse(split, symbolize_names: true) if split
end

def splits
def splits(split_names)
get_splits(split_names, false)
end

Expand Down Expand Up @@ -144,8 +114,92 @@ def splits_count
split_names.length
end

def get_feature_flags_by_sets(flag_sets)
sets_to_fetch = Array.new
flag_sets.each do |flag_set|
unless @flag_sets.flag_set_exist?(flag_set)
@config.logger.warn("Flag set #{flag_set} is not part of the configured flag set list, ignoring it.")
next
end
sets_to_fetch.push(flag_set)
end
to_return = Array.new
sets_to_fetch.each { |flag_set| to_return.concat(@flag_sets.get_flag_set(flag_set).to_a)}
to_return.uniq
end

def is_flag_set_exist(flag_set)
@flag_sets.flag_set_exist?(flag_set)
end

def flag_set_filter
@flag_set_filter
end

private

def add_feature_flag(split)
return unless split[:name]
existing_split = get_split(split[:name])

if(!existing_split)
increase_tt_name_count(split[:trafficTypeName])
elsif(existing_split[:trafficTypeName] != split[:trafficTypeName])
increase_tt_name_count(split[:trafficTypeName])
decrease_tt_name_count(existing_split[:trafficTypeName])
remove_from_flag_sets(existing_split)
end

if !split[:sets].nil?
for flag_set in split[:sets]
if !@flag_sets.flag_set_exist?(flag_set)
if @flag_set_filter.should_filter?
next
end
@flag_sets.add_flag_set(flag_set)
end
@flag_sets.add_feature_flag_to_flag_set(flag_set, split[:name])
end
end

@adapter.set_string(namespace_key(".split.#{split[:name]}"), split.to_json)
end

def remove_feature_flag(split)
tt_name = split[:trafficTypeName]

decrease_tt_name_count(split[:trafficTypeName])
remove_from_flag_sets(split)
@adapter.delete(namespace_key(".split.#{split[:name]}"))
end

def get_splits(names, symbolize_names = true)
splits = {}
split_names = names.map { |name| namespace_key(".split.#{name}") }
splits.merge!(
@adapter
.multiple_strings(split_names)
.map { |name, data| [name.gsub(namespace_key('.split.'), ''), data] }.to_h
)

splits.map do |name, data|
parsed_data = data ? JSON.parse(data, symbolize_names: true) : nil
split_name = symbolize_names ? name.to_sym : name
[split_name, parsed_data]
end.to_h
end

def remove_from_flag_sets(feature_flag)
if !feature_flag[:sets].nil?
for flag_set in feature_flag[:sets]
@flag_sets.remove_feature_flag_from_flag_set(flag_set, feature_flag[:name])
if is_flag_set_exist(flag_set) && @flag_sets.get_flag_set(flag_set).length == 0 && !@flag_set_filter.should_filter?
@flag_sets.remove_flag_set(flag_set)
end
end
end
end

def increase_tt_name_count(tt_name)
return unless tt_name

Expand Down
2 changes: 1 addition & 1 deletion lib/splitclient-rb/cache/stores/localhost_split_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def store_splits
def store_split(split)
@config.logger.debug("storing feature flag (#{split[:name]})") if @config.debug_enabled

@splits_repository.add_split(split)
@splits_repository.update([split], [], -1)
end

def load_features
Expand Down
Loading