splitclient-rb 1.0.2.wip → 8.11.1.pre.rc1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/pull_request_template.md +9 -0
  4. data/.github/workflows/ci.yml +90 -0
  5. data/.github/workflows/update-license-year.yml +45 -0
  6. data/.gitignore +20 -0
  7. data/.rubocop.yml +76 -0
  8. data/.simplecov +1 -0
  9. data/CHANGES.txt +361 -2
  10. data/CONTRIBUTORS-GUIDE.md +49 -0
  11. data/Gemfile +2 -0
  12. data/LICENSE +3 -36
  13. data/NOTICE.txt +5 -0
  14. data/README.md +59 -127
  15. data/Rakefile +27 -3
  16. data/ext/murmurhash/MurmurHash3.java +302 -0
  17. data/gemfiles/faraday_after_0.13.gemfile +7 -0
  18. data/gemfiles/faraday_before_0.13.gemfile +8 -0
  19. data/lib/murmurhash/base.rb +58 -0
  20. data/lib/murmurhash/murmurhash.jar +0 -0
  21. data/lib/murmurhash/murmurhash_mri.rb +3 -0
  22. data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +130 -0
  23. data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
  24. data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +137 -0
  25. data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +53 -0
  26. data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +178 -0
  27. data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +83 -0
  28. data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +70 -0
  29. data/lib/splitclient-rb/cache/filter/bloom_filter.rb +67 -0
  30. data/lib/splitclient-rb/cache/filter/filter_adapter.rb +32 -0
  31. data/lib/splitclient-rb/cache/filter/flag_set_filter.rb +40 -0
  32. data/lib/splitclient-rb/cache/hashers/impression_hasher.rb +34 -0
  33. data/lib/splitclient-rb/cache/observers/impression_observer.rb +22 -0
  34. data/lib/splitclient-rb/cache/observers/noop_impression_observer.rb +10 -0
  35. data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +45 -0
  36. data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +35 -0
  37. data/lib/splitclient-rb/cache/repositories/events_repository.rb +61 -0
  38. data/lib/splitclient-rb/cache/repositories/flag_sets/memory_repository.rb +40 -0
  39. data/lib/splitclient-rb/cache/repositories/flag_sets/redis_repository.rb +49 -0
  40. data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +56 -0
  41. data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +57 -0
  42. data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +23 -0
  43. data/lib/splitclient-rb/cache/repositories/repository.rb +24 -0
  44. data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
  45. data/lib/splitclient-rb/cache/repositories/segments_repository.rb +124 -0
  46. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +296 -0
  47. data/lib/splitclient-rb/cache/routers/impression_router.rb +60 -0
  48. data/lib/splitclient-rb/cache/senders/events_sender.rb +42 -0
  49. data/lib/splitclient-rb/cache/senders/impressions_adapter/memory_sender.rb +71 -0
  50. data/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb +69 -0
  51. data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +43 -0
  52. data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +95 -0
  53. data/lib/splitclient-rb/cache/senders/impressions_sender.rb +52 -0
  54. data/lib/splitclient-rb/cache/senders/impressions_sender_adapter.rb +21 -0
  55. data/lib/splitclient-rb/cache/senders/localhost_repo_cleaner.rb +47 -0
  56. data/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +95 -0
  57. data/lib/splitclient-rb/cache/stores/localhost_split_store.rb +110 -0
  58. data/lib/splitclient-rb/cache/stores/store_utils.rb +13 -0
  59. data/lib/splitclient-rb/clients/split_client.rb +530 -0
  60. data/lib/splitclient-rb/constants.rb +16 -0
  61. data/lib/splitclient-rb/engine/api/client.rb +87 -0
  62. data/lib/splitclient-rb/engine/api/events.rb +57 -0
  63. data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +58 -0
  64. data/lib/splitclient-rb/engine/api/impressions.rb +79 -0
  65. data/lib/splitclient-rb/engine/api/segments.rb +78 -0
  66. data/lib/splitclient-rb/engine/api/splits.rb +139 -0
  67. data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
  68. data/lib/splitclient-rb/engine/auth_api_client.rb +100 -0
  69. data/lib/splitclient-rb/engine/back_off.rb +26 -0
  70. data/lib/splitclient-rb/engine/common/impressions_counter.rb +45 -0
  71. data/lib/splitclient-rb/engine/common/impressions_manager.rb +165 -0
  72. data/lib/splitclient-rb/engine/common/noop_impressions_counter.rb +27 -0
  73. data/lib/{splitclient-engine → splitclient-rb/engine}/evaluator/splitter.rb +35 -22
  74. data/lib/splitclient-rb/engine/events/events_delivery.rb +20 -0
  75. data/lib/splitclient-rb/engine/events/events_manager.rb +194 -0
  76. data/lib/splitclient-rb/engine/events/events_manager_config.rb +96 -0
  77. data/lib/splitclient-rb/engine/events/events_task.rb +50 -0
  78. data/lib/splitclient-rb/engine/events/noop_events_queue.rb +13 -0
  79. data/lib/splitclient-rb/engine/fallback_treatment_calculator.rb +48 -0
  80. data/lib/splitclient-rb/engine/impressions/noop_unique_keys_tracker.rb +17 -0
  81. data/lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb +144 -0
  82. data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/all_keys_matcher.rb +10 -21
  83. data/lib/splitclient-rb/engine/matchers/between_matcher.rb +47 -0
  84. data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
  85. data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/combiners.rb +4 -6
  86. data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +80 -0
  87. data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +20 -0
  88. data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +16 -0
  89. data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +43 -0
  90. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +26 -0
  91. data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +42 -0
  92. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +37 -0
  93. data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +44 -0
  94. data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
  95. data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +16 -0
  96. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +43 -0
  97. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +28 -0
  98. data/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +36 -0
  99. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +43 -0
  100. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
  101. data/lib/splitclient-rb/engine/matchers/matcher.rb +52 -0
  102. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +29 -0
  103. data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +47 -0
  104. data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +22 -0
  105. data/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb +31 -0
  106. data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +78 -0
  107. data/lib/splitclient-rb/engine/matchers/semver.rb +201 -0
  108. data/lib/splitclient-rb/engine/matchers/set_matcher.rb +27 -0
  109. data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +40 -0
  110. data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +28 -0
  111. data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
  112. data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +66 -0
  113. data/lib/splitclient-rb/engine/models/evaluation_options.rb +9 -0
  114. data/lib/splitclient-rb/engine/models/event_active_subscriptions.rb +14 -0
  115. data/lib/splitclient-rb/engine/models/events_metadata.rb +10 -0
  116. data/lib/splitclient-rb/engine/models/fallback_treatment.rb +11 -0
  117. data/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb +36 -0
  118. data/lib/splitclient-rb/engine/models/label.rb +10 -0
  119. data/lib/splitclient-rb/engine/models/sdk_event.rb +4 -0
  120. data/lib/splitclient-rb/engine/models/sdk_event_type.rb +4 -0
  121. data/lib/splitclient-rb/engine/models/sdk_internal_event.rb +8 -0
  122. data/lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb +16 -0
  123. data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
  124. data/lib/splitclient-rb/engine/models/split.rb +17 -0
  125. data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
  126. data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
  127. data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
  128. data/lib/splitclient-rb/engine/parser/condition.rb +271 -0
  129. data/lib/splitclient-rb/engine/parser/evaluator.rb +112 -0
  130. data/lib/{splitclient-engine → splitclient-rb/engine}/parser/partition.rb +2 -4
  131. data/lib/splitclient-rb/engine/push_manager.rb +66 -0
  132. data/lib/splitclient-rb/engine/status_manager.rb +39 -0
  133. data/lib/splitclient-rb/engine/sync_manager.rb +183 -0
  134. data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
  135. data/lib/splitclient-rb/exceptions.rb +26 -0
  136. data/lib/splitclient-rb/helpers/decryption_helper.rb +25 -0
  137. data/lib/splitclient-rb/helpers/evaluator_helper.rb +37 -0
  138. data/lib/splitclient-rb/helpers/repository_helper.rb +61 -0
  139. data/lib/splitclient-rb/helpers/thread_helper.rb +24 -0
  140. data/lib/splitclient-rb/helpers/util.rb +26 -0
  141. data/lib/splitclient-rb/managers/split_manager.rb +121 -0
  142. data/lib/splitclient-rb/spec.rb +9 -0
  143. data/lib/splitclient-rb/split_config.rb +591 -47
  144. data/lib/splitclient-rb/split_factory.rb +284 -0
  145. data/lib/splitclient-rb/split_factory_builder.rb +7 -0
  146. data/lib/splitclient-rb/split_factory_registry.rb +63 -0
  147. data/lib/splitclient-rb/split_logger.rb +23 -0
  148. data/lib/splitclient-rb/sse/event_source/client.rb +264 -0
  149. data/lib/splitclient-rb/sse/event_source/event_parser.rb +65 -0
  150. data/lib/splitclient-rb/sse/event_source/event_types.rb +15 -0
  151. data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
  152. data/lib/splitclient-rb/sse/notification_manager_keeper.rb +84 -0
  153. data/lib/splitclient-rb/sse/notification_processor.rb +48 -0
  154. data/lib/splitclient-rb/sse/sse_handler.rb +44 -0
  155. data/lib/splitclient-rb/sse/workers/segments_worker.rb +62 -0
  156. data/lib/splitclient-rb/sse/workers/splits_worker.rb +153 -0
  157. data/lib/splitclient-rb/telemetry/domain/constants.rb +48 -0
  158. data/lib/splitclient-rb/telemetry/domain/structs.rb +35 -0
  159. data/lib/splitclient-rb/telemetry/evaluation_consumer.rb +14 -0
  160. data/lib/splitclient-rb/telemetry/evaluation_producer.rb +21 -0
  161. data/lib/splitclient-rb/telemetry/init_consumer.rb +14 -0
  162. data/lib/splitclient-rb/telemetry/init_producer.rb +19 -0
  163. data/lib/splitclient-rb/telemetry/memory/memory_evaluation_consumer.rb +32 -0
  164. data/lib/splitclient-rb/telemetry/memory/memory_evaluation_producer.rb +24 -0
  165. data/lib/splitclient-rb/telemetry/memory/memory_init_consumer.rb +28 -0
  166. data/lib/splitclient-rb/telemetry/memory/memory_init_producer.rb +34 -0
  167. data/lib/splitclient-rb/telemetry/memory/memory_runtime_consumer.rb +119 -0
  168. data/lib/splitclient-rb/telemetry/memory/memory_runtime_producer.rb +87 -0
  169. data/lib/splitclient-rb/telemetry/memory/memory_synchronizer.rb +213 -0
  170. data/lib/splitclient-rb/telemetry/redis/redis_evaluation_producer.rb +38 -0
  171. data/lib/splitclient-rb/telemetry/redis/redis_init_producer.rb +37 -0
  172. data/lib/splitclient-rb/telemetry/redis/redis_synchronizer.rb +27 -0
  173. data/lib/splitclient-rb/telemetry/runtime_consumer.rb +25 -0
  174. data/lib/splitclient-rb/telemetry/runtime_producer.rb +25 -0
  175. data/lib/splitclient-rb/telemetry/storages/memory.rb +159 -0
  176. data/lib/splitclient-rb/telemetry/sync_task.rb +36 -0
  177. data/lib/splitclient-rb/telemetry/synchronizer.rb +33 -0
  178. data/lib/splitclient-rb/utilitites.rb +49 -0
  179. data/lib/splitclient-rb/validators.rb +361 -0
  180. data/lib/splitclient-rb/version.rb +1 -1
  181. data/lib/splitclient-rb.rb +177 -25
  182. data/sonar-project.properties +6 -0
  183. data/splitclient-rb.gemspec +53 -26
  184. data/tasks/benchmark_get_treatment.rake +31 -17
  185. data/tasks/irb.rake +6 -0
  186. metadata +411 -86
  187. data/NEWS +0 -9
  188. data/lib/splitclient-cache/local_store.rb +0 -45
  189. data/lib/splitclient-engine/impressions/impressions.rb +0 -79
  190. data/lib/splitclient-engine/matchers/between_matcher.rb +0 -48
  191. data/lib/splitclient-engine/matchers/combining_matcher.rb +0 -94
  192. data/lib/splitclient-engine/matchers/equal_to_matcher.rb +0 -48
  193. data/lib/splitclient-engine/matchers/greater_than_or_equal_to_matcher.rb +0 -48
  194. data/lib/splitclient-engine/matchers/less_than_or_equal_to_matcher.rb +0 -48
  195. data/lib/splitclient-engine/matchers/negation_matcher.rb +0 -54
  196. data/lib/splitclient-engine/matchers/user_defined_segment_matcher.rb +0 -61
  197. data/lib/splitclient-engine/matchers/whitelist_matcher.rb +0 -64
  198. data/lib/splitclient-engine/metrics/binary_search_latency_tracker.rb +0 -122
  199. data/lib/splitclient-engine/metrics/metrics.rb +0 -158
  200. data/lib/splitclient-engine/parser/condition.rb +0 -143
  201. data/lib/splitclient-engine/parser/segment.rb +0 -84
  202. data/lib/splitclient-engine/parser/segment_parser.rb +0 -46
  203. data/lib/splitclient-engine/parser/split.rb +0 -68
  204. data/lib/splitclient-engine/parser/split_adapter.rb +0 -433
  205. data/lib/splitclient-engine/parser/split_parser.rb +0 -139
  206. data/lib/splitclient-engine/partitions/treatments.rb +0 -40
  207. data/lib/splitclient-rb/split_client.rb +0 -156
  208. data/lib/splitclient-rb_utilitites.rb +0 -26
  209. data/tasks/console.rake +0 -4
  210. data/tasks/rspec.rake +0 -3
@@ -0,0 +1,61 @@
1
+ module SplitIoClient
2
+ module Cache
3
+ module Repositories
4
+ # Repository which forwards events interface to the selected adapter
5
+ class EventsRepository < Repository
6
+ extend Forwardable
7
+ def_delegators :@repository, :add, :clear, :batch
8
+
9
+ def initialize(config, api_key, telemetry_runtime_producer)
10
+ super(config)
11
+ @repository = case @config.events_adapter.class.to_s
12
+ when 'SplitIoClient::Cache::Adapters::MemoryAdapter'
13
+ Repositories::Events::MemoryRepository.new(@config, telemetry_runtime_producer)
14
+ when 'SplitIoClient::Cache::Adapters::RedisAdapter'
15
+ Repositories::Events::RedisRepository.new(@config)
16
+ end
17
+
18
+ @api_key = api_key
19
+ @telemetry_runtime_producer = telemetry_runtime_producer
20
+ end
21
+
22
+ def post_events
23
+ events_api.post(self.clear)
24
+ rescue StandardError => e
25
+ @config.log_found_exception(__method__.to_s, e)
26
+ end
27
+
28
+ def empty?
29
+ @repository.empty?
30
+ end
31
+
32
+ protected
33
+
34
+ def metadata
35
+ {
36
+ s: "#{@config.language}-#{@config.version}",
37
+ i: @config.machine_ip,
38
+ n: @config.machine_name
39
+ }
40
+ end
41
+
42
+ def event(key, traffic_type, event_type, time, value, properties)
43
+ {
44
+ key: key,
45
+ trafficTypeName: traffic_type,
46
+ eventTypeId: event_type,
47
+ value: value,
48
+ timestamp: time,
49
+ properties: properties
50
+ }.reject { |_, v| v.nil? }
51
+ end
52
+
53
+ private
54
+
55
+ def events_api
56
+ @events_api ||= SplitIoClient::Api::Events.new(@api_key, @config, @telemetry_runtime_producer)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,40 @@
1
+ require 'concurrent'
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ class MemoryFlagSetsRepository
7
+ def initialize(flag_sets = [])
8
+ @sets_feature_flag_map = {}
9
+ flag_sets.each{ |flag_set| @sets_feature_flag_map[flag_set] = Set[] }
10
+ end
11
+
12
+ def flag_set_exist?(flag_set)
13
+ @sets_feature_flag_map.key?(flag_set)
14
+ end
15
+
16
+ def get_flag_sets(flag_sets)
17
+ to_return = Array.new
18
+ flag_sets.each { |flag_set| to_return.concat(@sets_feature_flag_map[flag_set].to_a)}
19
+ to_return.uniq
20
+ end
21
+
22
+ def add_flag_set(flag_set)
23
+ @sets_feature_flag_map[flag_set] = Set[] if !flag_set_exist?(flag_set)
24
+ end
25
+
26
+ def remove_flag_set(flag_set)
27
+ @sets_feature_flag_map.delete(flag_set) if flag_set_exist?(flag_set)
28
+ end
29
+
30
+ def add_feature_flag_to_flag_set(flag_set, feature_flag)
31
+ @sets_feature_flag_map[flag_set].add(feature_flag) if flag_set_exist?(flag_set)
32
+ end
33
+
34
+ def remove_feature_flag_from_flag_set(flag_set, feature_flag)
35
+ @sets_feature_flag_map[flag_set].delete(feature_flag) if flag_set_exist?(flag_set)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ require 'concurrent'
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ class RedisFlagSetsRepository < Repository
7
+
8
+ def initialize(config)
9
+ super(config)
10
+ @adapter = SplitIoClient::Cache::Adapters::RedisAdapter.new(@config.redis_url)
11
+ end
12
+
13
+ def flag_set_exist?(flag_set)
14
+ @adapter.exists?(namespace_key(".flagSet.#{flag_set}"))
15
+ end
16
+
17
+ def get_flag_sets(flag_sets)
18
+ result = @adapter.redis.pipelined do |pipeline|
19
+ flag_sets.each do |flag_set|
20
+ pipeline.smembers(namespace_key(".flagSet.#{flag_set}"))
21
+ end
22
+ end
23
+ to_return = Array.new
24
+ result.each do |flag_set|
25
+ flag_set.each { |feature_flag_name| to_return.push(feature_flag_name.to_s)}
26
+ end
27
+ to_return.uniq
28
+ end
29
+
30
+ def add_flag_set(flag_set)
31
+ # not implemented
32
+ end
33
+
34
+ def remove_flag_set(flag_set)
35
+ # not implemented
36
+ end
37
+
38
+ def add_feature_flag_to_flag_set(flag_set, feature_flag)
39
+ # not implemented
40
+ end
41
+
42
+ def remove_feature_flag_from_flag_set(flag_set, feature_flag)
43
+ # not implemented
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ module Impressions
7
+ class MemoryRepository < ImpressionsRepository
8
+ def initialize(config)
9
+ @config = config
10
+ @adapter = @config.impressions_adapter
11
+ end
12
+
13
+ def add_bulk(impressions)
14
+ return 0 if impressions.nil?
15
+
16
+ count = 0
17
+ impressions.each do |impression|
18
+ @adapter.add_to_queue(impression)
19
+ count += 1
20
+ end
21
+
22
+ 0
23
+ rescue ThreadError # queue is full
24
+ if random_sampler.rand(1..1000) <= 2 # log only 0.2 % of the time
25
+ @config.logger.warn("Dropping impressions. Current size is \
26
+ #{@config.impressions_queue_size}. " \
27
+ 'Consider increasing impressions_queue_size')
28
+ end
29
+
30
+ impressions.length - count
31
+ end
32
+
33
+ def batch
34
+ return [] if @config.impressions_bulk_size.zero?
35
+
36
+ @adapter.get_batch(@config.impressions_bulk_size)
37
+ end
38
+
39
+ def clear
40
+ @adapter.clear
41
+ end
42
+
43
+ def empty?
44
+ @adapter.empty?
45
+ end
46
+
47
+ private
48
+
49
+ def random_sampler
50
+ @random_sampler ||= Random.new
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ module Impressions
7
+ class RedisRepository < ImpressionsRepository
8
+ EXPIRE_SECONDS = 3600
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ @adapter = @config.impressions_adapter
13
+ end
14
+
15
+ def add_bulk(impressions)
16
+ impressions_json = impressions.map do |impression|
17
+ impression[:i][:properties] = impression[:i][:properties].to_json.to_s unless impression[:i][:properties].nil?
18
+ impression.to_json
19
+ end
20
+
21
+ impressions_list_size = @adapter.add_to_queue(key, impressions_json)
22
+
23
+ # Synchronizer might not be running
24
+ @adapter.expire(key, EXPIRE_SECONDS) if impressions_json.size == impressions_list_size
25
+ 0
26
+ rescue StandardError => e
27
+ @config.logger.error("Exception while add_bulk: #{e}")
28
+ 0
29
+ end
30
+
31
+ def get_impressions(number_of_impressions = 0)
32
+ @adapter.get_from_queue(key, number_of_impressions).map do |e|
33
+ impression = JSON.parse(e, symbolize_names: true)
34
+ impression[:i][:f] = impression[:i][:f].to_sym
35
+ impression
36
+ end
37
+ rescue StandardError => e
38
+ @config.logger.error("Exception while clearing impressions cache: #{e}")
39
+ []
40
+ end
41
+
42
+ def batch
43
+ get_impressions(@config.impressions_bulk_size)
44
+ end
45
+
46
+ def clear
47
+ get_impressions
48
+ end
49
+
50
+ def key
51
+ @key ||= namespace_key('.impressions')
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ # Repository which forwards impressions interface to the selected adapter
7
+ class ImpressionsRepository < Repository
8
+ extend Forwardable
9
+ def_delegators :@repository, :add_bulk, :batch, :clear, :empty?
10
+
11
+ def initialize(config)
12
+ super(config)
13
+ @repository = case @config.impressions_adapter.class.to_s
14
+ when 'SplitIoClient::Cache::Adapters::MemoryAdapter'
15
+ Repositories::Impressions::MemoryRepository.new(@config)
16
+ when 'SplitIoClient::Cache::Adapters::RedisAdapter'
17
+ Repositories::Impressions::RedisRepository.new(@config)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module SplitIoClient
2
+ module Cache
3
+ class Repository
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def set_string(key, str)
10
+ @adapter.set_string(namespace_key(key), str)
11
+ end
12
+
13
+ def string(key)
14
+ @adapter.string(namespace_key(key))
15
+ end
16
+
17
+ protected
18
+
19
+ def namespace_key(key = '')
20
+ "#{@config.redis_namespace}#{key}"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,136 @@
1
+ require 'concurrent'
2
+
3
+ module SplitIoClient
4
+ module Cache
5
+ module Repositories
6
+ class RuleBasedSegmentsRepository < Repository
7
+ attr_reader :adapter
8
+ DEFAULT_CONDITIONS_TEMPLATE = [{
9
+ conditionType: "ROLLOUT",
10
+ matcherGroup: {
11
+ combiner: "AND",
12
+ matchers: [
13
+ {
14
+ keySelector: nil,
15
+ matcherType: "ALL_KEYS",
16
+ negate: false,
17
+ userDefinedSegmentMatcherData: nil,
18
+ whitelistMatcherData: nil,
19
+ unaryNumericMatcherData: nil,
20
+ betweenMatcherData: nil,
21
+ dependencyMatcherData: nil,
22
+ booleanMatcherData: nil,
23
+ stringMatcherData: nil
24
+ }]
25
+ }
26
+ }]
27
+ TILL_PREFIX = '.rbsegments.till'
28
+ RB_SEGMENTS_PREFIX = '.rbsegment.'
29
+ REGISTERED_PREFIX = '.segments.registered'
30
+
31
+ def initialize(config, internal_events_queue)
32
+ super(config)
33
+ @adapter = case @config.cache_adapter.class.to_s
34
+ when 'SplitIoClient::Cache::Adapters::RedisAdapter'
35
+ SplitIoClient::Cache::Adapters::CacheAdapter.new(@config)
36
+ else
37
+ @config.cache_adapter
38
+ end
39
+ unless @config.mode.equal?(:consumer)
40
+ @adapter.set_string(namespace_key(TILL_PREFIX), '-1')
41
+ @adapter.initialize_map(namespace_key(REGISTERED_PREFIX))
42
+ end
43
+ @internal_events_queue = internal_events_queue
44
+ end
45
+
46
+ def update(to_add, to_delete, new_change_number)
47
+ to_add.each{ |rule_based_segment| add_rule_based_segment(rule_based_segment) }
48
+ to_delete.each{ |rule_based_segment| remove_rule_based_segment(rule_based_segment) }
49
+ set_change_number(new_change_number)
50
+
51
+ if to_add.length > 0 || to_delete.length > 0
52
+ @internal_events_queue.push(
53
+ SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
54
+ SplitIoClient::Engine::Models::SdkInternalEvent::RB_SEGMENTS_UPDATED,
55
+ SplitIoClient::Engine::Models::EventsMetadata.new(
56
+ SplitIoClient::Engine::Models::SdkEventType::SEGMENTS_UPDATE,
57
+ []
58
+ )
59
+ )
60
+ )
61
+ end
62
+ end
63
+
64
+ def get_rule_based_segment(name)
65
+ rule_based_segment = @adapter.string(namespace_key("#{RB_SEGMENTS_PREFIX}#{name}"))
66
+
67
+ JSON.parse(rule_based_segment, symbolize_names: true) if rule_based_segment
68
+ end
69
+
70
+ def rule_based_segment_names
71
+ @adapter.find_strings_by_prefix(namespace_key(RB_SEGMENTS_PREFIX))
72
+ .map { |rule_based_segment_names| rule_based_segment_names.gsub(namespace_key(RB_SEGMENTS_PREFIX), '') }
73
+ end
74
+
75
+ def set_change_number(since)
76
+ @adapter.set_string(namespace_key(TILL_PREFIX), since)
77
+ end
78
+
79
+ def get_change_number
80
+ @adapter.string(namespace_key(TILL_PREFIX))
81
+ end
82
+
83
+ def set_segment_names(names)
84
+ return if names.nil? || names.empty?
85
+
86
+ names.each do |name|
87
+ @adapter.add_to_set(namespace_key(REGISTERED_PREFIX), name)
88
+ end
89
+ end
90
+
91
+ def exists?(name)
92
+ @adapter.exists?(namespace_key("#{RB_SEGMENTS_PREFIX}#{name}"))
93
+ end
94
+
95
+ def clear
96
+ @adapter.clear(namespace_key)
97
+ end
98
+
99
+ def contains?(segment_names)
100
+ return false if rule_based_segment_names.empty?
101
+
102
+ return segment_names.to_set.subset?(rule_based_segment_names.to_set)
103
+ end
104
+
105
+ private
106
+
107
+ def add_rule_based_segment(rule_based_segment)
108
+ return unless rule_based_segment[:name]
109
+
110
+ if check_undefined_matcher(rule_based_segment)
111
+ @config.logger.warn("Rule based segment #{rule_based_segment[:name]} has undefined matcher, setting conditions to default template.")
112
+ rule_based_segment[:conditions] = RuleBasedSegmentsRepository::DEFAULT_CONDITIONS_TEMPLATE
113
+ end
114
+
115
+ @adapter.set_string(namespace_key("#{RB_SEGMENTS_PREFIX}#{rule_based_segment[:name]}"), rule_based_segment.to_json)
116
+ end
117
+
118
+ def check_undefined_matcher(rule_based_segment)
119
+ for condition in rule_based_segment[:conditions]
120
+ for matcher in condition[:matcherGroup][:matchers]
121
+ if !SplitIoClient::Condition.instance_methods(false).map(&:to_s).include?("matcher_#{matcher[:matcherType].downcase}")
122
+ @config.logger.error("Detected undefined matcher #{matcher[:matcherType].downcase} in feature flag #{rule_based_segment[:name]}")
123
+ return true
124
+ end
125
+ end
126
+ end
127
+ return false
128
+ end
129
+
130
+ def remove_rule_based_segment(rule_based_segment)
131
+ @adapter.delete(namespace_key("#{RB_SEGMENTS_PREFIX}#{rule_based_segment[:name]}"))
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,124 @@
1
+ module SplitIoClient
2
+ module Cache
3
+ module Repositories
4
+ class SegmentsRepository < Repository
5
+ KEYS_SLICE = 3000
6
+
7
+ attr_reader :adapter
8
+
9
+ def initialize(config, internal_events_queue)
10
+ super(config)
11
+ @adapter = case @config.cache_adapter.class.to_s
12
+ when 'SplitIoClient::Cache::Adapters::RedisAdapter'
13
+ SplitIoClient::Cache::Adapters::CacheAdapter.new(@config)
14
+ else
15
+ @config.cache_adapter
16
+ end
17
+ @adapter.set_bool(namespace_key('.ready'), false) unless @config.mode.equal?(:consumer)
18
+ @internal_events_queue = internal_events_queue
19
+ end
20
+
21
+ # Receives segment data, adds and removes segements from the store
22
+ def add_to_segment(segment)
23
+ name = segment[:name]
24
+
25
+ @adapter.initialize_set(segment_data(name)) unless @adapter.exists?(segment_data(name))
26
+ add_keys(name, segment[:added])
27
+ remove_keys(name, segment[:removed])
28
+ if segment[:added].length > 0 || segment[:removed].length > 0
29
+ @internal_events_queue.push(
30
+ SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
31
+ SplitIoClient::Engine::Models::SdkInternalEvent::SEGMENTS_UPDATED,
32
+ SplitIoClient::Engine::Models::EventsMetadata.new(
33
+ SplitIoClient::Engine::Models::SdkEventType::SEGMENTS_UPDATE,
34
+ []
35
+ )
36
+ )
37
+ )
38
+ end
39
+ end
40
+
41
+ def get_segment_keys(name)
42
+ @adapter.get_set(segment_data(name))
43
+ end
44
+
45
+ def in_segment?(name, key)
46
+ @adapter.in_set?(segment_data(name), key)
47
+ end
48
+
49
+ def used_segment_names
50
+ @adapter.get_set(namespace_key('.segments.registered'))
51
+ end
52
+
53
+ def set_change_number(name, last_change)
54
+ @adapter.set_string(namespace_key(".segment.#{name}.till"), last_change)
55
+ end
56
+
57
+ def get_change_number(name)
58
+ @adapter.string(namespace_key(".segment.#{name}.till")) || -1
59
+ end
60
+
61
+ def ready?
62
+ @adapter.string(namespace_key('.segments.ready')).to_i != -1
63
+ end
64
+
65
+ def not_ready!
66
+ @adapter.set_string(namespace_key('.segments.ready'), -1)
67
+ end
68
+
69
+ def ready!
70
+ @adapter.set_string(namespace_key('.segments.ready'), Time.now.utc.to_i)
71
+ end
72
+
73
+ def clear
74
+ @adapter.clear(namespace_key)
75
+ end
76
+
77
+ def segments_count
78
+ used_segment_names.length
79
+ end
80
+
81
+ def segment_keys_count
82
+ names = used_segment_names
83
+
84
+ keys = 0
85
+
86
+ names.each do |name|
87
+ segment_keys = get_segment_keys(name)
88
+ keys += segment_keys.length
89
+ end
90
+
91
+ keys
92
+ rescue StandardError => e
93
+ @config.log_found_exception(__method__.to_s, e)
94
+ 0
95
+ end
96
+
97
+ def contains?(segment_names)
98
+ if segment_names.empty?
99
+ return false
100
+ end
101
+ return segment_names.to_set.subset?(used_segment_names.to_set)
102
+ end
103
+
104
+ private
105
+
106
+ def segment_data(name)
107
+ namespace_key(".segment.#{name}")
108
+ end
109
+
110
+ def add_keys(name, keys)
111
+ keys.each_slice(KEYS_SLICE) do |keys_slice|
112
+ @adapter.add_to_set(segment_data(name), keys_slice)
113
+ end
114
+ end
115
+
116
+ def remove_keys(name, keys)
117
+ keys.each_slice(KEYS_SLICE) do |keys_slice|
118
+ @adapter.delete_from_set(segment_data(name), keys_slice)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end