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,65 @@
1
+ # frozen_string_literal: false
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module EventSource
6
+ class EventParser
7
+ BAD_REQUEST_CODE = 400
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ end
12
+
13
+ def parse(raw_event)
14
+ type = nil
15
+ events = []
16
+ buffer = read_partial_data(raw_event)
17
+
18
+ buffer.each do |d|
19
+ splited_data = d.split(':')
20
+
21
+ case splited_data[0]
22
+ when 'event'
23
+ type = splited_data[1].strip
24
+ when 'data'
25
+ data = parse_event_data(d, type)
26
+ events << StreamData.new(type, data[:client_id], data[:data], data[:channel]) unless type.nil? || data[:data].nil?
27
+ end
28
+ end
29
+
30
+ events
31
+ rescue StandardError => e
32
+ @config.logger.debug("Error during parsing a event: #{e.inspect}") if @config.debug_enabled
33
+ []
34
+ end
35
+
36
+ def first_event(raw_data)
37
+ raw_data.split("\n")[0].split(' ')[1].to_i
38
+ rescue StandardError => e
39
+ @config.logger.error("Error parsing first event: #{e.inspect}")
40
+ BAD_REQUEST_CODE
41
+ end
42
+
43
+ private
44
+
45
+ def parse_event_data(data, type)
46
+ data_value = data.sub('data:', '')
47
+ event_data = JSON.parse(data_value.strip)
48
+ client_id = event_data['clientId']&.strip
49
+ channel = event_data['channel']&.strip
50
+ parsed_data = JSON.parse(event_data['data']) unless type == 'error'
51
+ parsed_data = event_data if type == 'error'
52
+
53
+ { client_id: client_id, channel: channel, data: parsed_data }
54
+ end
55
+
56
+ def read_partial_data(data)
57
+ buffer = ''
58
+ buffer << data
59
+ buffer.chomp!
60
+ buffer.split("\n")
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module EventSource
6
+ class EventTypes
7
+ SPLIT_UPDATE = 'SPLIT_UPDATE'
8
+ SPLIT_KILL = 'SPLIT_KILL'
9
+ SEGMENT_UPDATE = 'SEGMENT_UPDATE'
10
+ CONTROL = 'CONTROL'
11
+ RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: false
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module EventSource
6
+ class StreamData
7
+ attr_reader :event_type, :channel, :data, :client_id
8
+
9
+ def initialize(event_type, client_id, data, channel)
10
+ @event_type = event_type
11
+ @client_id = client_id
12
+ @data = data
13
+ @channel = channel&.gsub(SplitIoClient::Constants::OCCUPANCY_CHANNEL_PREFIX, '')
14
+ end
15
+
16
+ def occupancy?
17
+ @channel.include? 'control'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'concurrent'
4
+
5
+ module SplitIoClient
6
+ module SSE
7
+ class NotificationManagerKeeper
8
+ DISABLED = 0
9
+ ENABLED = 1
10
+ PAUSED = 2
11
+
12
+ def initialize(config, telemetry_runtime_producer, status_queue)
13
+ @config = config
14
+ @telemetry_runtime_producer = telemetry_runtime_producer
15
+ @status_queue = status_queue
16
+ @publisher_available = Concurrent::AtomicBoolean.new(true)
17
+ @publishers_pri = Concurrent::AtomicFixnum.new(2)
18
+ @publishers_sec = Concurrent::AtomicFixnum.new(2)
19
+ end
20
+
21
+ def handle_incoming_occupancy_event(event)
22
+ if event.data['type'] == 'CONTROL'
23
+ process_event_control(event.data['controlType'])
24
+ else
25
+ process_event_occupancy(event.channel, event.data['metrics']['publishers'])
26
+ end
27
+ rescue StandardError => e
28
+ @config.logger.error(e)
29
+ end
30
+
31
+ private
32
+
33
+ def process_event_control(type)
34
+ case type
35
+ when 'STREAMING_PAUSED'
36
+ @telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::STREAMING_STATUS, PAUSED)
37
+ push_status(Constants::PUSH_SUBSYSTEM_DOWN)
38
+ when 'STREAMING_RESUMED'
39
+ @telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::STREAMING_STATUS, ENABLED)
40
+ push_status(Constants::PUSH_SUBSYSTEM_READY) if @publisher_available.value
41
+ when 'STREAMING_DISABLED'
42
+ @telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::STREAMING_STATUS, DISABLED)
43
+ push_status(Constants::PUSH_SUBSYSTEM_OFF)
44
+ else
45
+ @config.logger.error("Incorrect event type: #{incoming_notification}") if @config.debug_enabled
46
+ end
47
+ end
48
+
49
+ def process_event_occupancy(channel, publishers)
50
+ @config.logger.debug("Processed occupancy event with #{publishers} publishers. Channel: #{channel}") if @config.debug_enabled
51
+
52
+ update_publishers(channel, publishers)
53
+
54
+ if !are_publishers_available? && @publisher_available.value
55
+ @publisher_available.make_false
56
+ push_status(Constants::PUSH_SUBSYSTEM_DOWN)
57
+ elsif are_publishers_available? && !@publisher_available.value
58
+ @publisher_available.make_true
59
+ push_status(Constants::PUSH_SUBSYSTEM_READY)
60
+ end
61
+ end
62
+
63
+ def update_publishers(channel, publishers)
64
+ case channel
65
+ when Constants::CONTROL_PRI
66
+ @telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::OCCUPANCY_PRI, publishers)
67
+ @publishers_pri.value = publishers
68
+ when Constants::CONTROL_SEC
69
+ @telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::OCCUPANCY_SEC, publishers)
70
+ @publishers_sec.value = publishers
71
+ end
72
+ end
73
+
74
+ def are_publishers_available?
75
+ @publishers_pri.value.positive? || @publishers_sec.value.positive?
76
+ end
77
+
78
+ def push_status(status)
79
+ @config.logger.debug("Pushing occupancy status: #{status}") if @config.debug_enabled
80
+ @status_queue.push(status)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ class NotificationProcessor
6
+ def initialize(config, splits_worker, segments_worker)
7
+ @config = config
8
+ @splits_worker = splits_worker
9
+ @segments_worker = segments_worker
10
+ end
11
+
12
+ def process(incoming_notification)
13
+ case incoming_notification.data['type']
14
+ when SSE::EventSource::EventTypes::SPLIT_UPDATE
15
+ process_split_update(incoming_notification)
16
+ when SSE::EventSource::EventTypes::RB_SEGMENT_UPDATE
17
+ process_split_update(incoming_notification)
18
+ when SSE::EventSource::EventTypes::SPLIT_KILL
19
+ process_split_kill(incoming_notification)
20
+ when SSE::EventSource::EventTypes::SEGMENT_UPDATE
21
+ process_segment_update(incoming_notification)
22
+ else
23
+ @config.logger.error("Incorrect event type: #{incoming_notification}")
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def process_split_update(notification)
30
+ @config.logger.debug("#{notification.event_type} notification received: #{notification}") if @config.debug_enabled
31
+ @splits_worker.add_to_queue(notification)
32
+ end
33
+
34
+ def process_split_kill(notification)
35
+ @config.logger.debug("SPLIT KILL notification received: #{notification}") if @config.debug_enabled
36
+ @splits_worker.add_to_queue(notification)
37
+ end
38
+
39
+ def process_segment_update(notification)
40
+ @config.logger.debug("SEGMENT UPDATE notification received: #{notification}") if @config.debug_enabled
41
+ change_number = notification.data['changeNumber']
42
+ segment_name = notification.data['segmentName']
43
+
44
+ @segments_worker.add_to_queue(change_number, segment_name)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ class SSEHandler
6
+ attr_reader :sse_client
7
+
8
+ def initialize(config,
9
+ splits_worker,
10
+ segments_worker,
11
+ sse_client)
12
+ @config = config
13
+ @splits_worker = splits_worker
14
+ @segments_worker = segments_worker
15
+ @sse_client = sse_client
16
+ end
17
+
18
+ def start(token_jwt, channels)
19
+ @sse_client.start("#{@config.streaming_service_url}?channels=#{channels}&v=1.1&accessToken=#{token_jwt}")
20
+ end
21
+
22
+ def stop
23
+ @sse_client.close(Constants::PUSH_FORCED_STOP)
24
+ stop_workers
25
+ rescue StandardError => e
26
+ @config.logger.debug("SSEHandler stop error: #{e.inspect}") if @config.debug_enabled
27
+ end
28
+
29
+ def connected?
30
+ @sse_client&.connected? || false
31
+ end
32
+
33
+ def start_workers
34
+ @splits_worker.start
35
+ @segments_worker.start
36
+ end
37
+
38
+ def stop_workers
39
+ @splits_worker.stop
40
+ @segments_worker.stop
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module Workers
6
+ class SegmentsWorker
7
+ def initialize(synchronizer, config, segments_repository)
8
+ @synchronizer = synchronizer
9
+ @config = config
10
+ @segments_repository = segments_repository
11
+ @queue = Queue.new
12
+ @running = Concurrent::AtomicBoolean.new(false)
13
+ end
14
+
15
+ def add_to_queue(change_number, segment_name)
16
+ item = { change_number: change_number, segment_name: segment_name }
17
+ @config.logger.debug("SegmentsWorker add to queue #{item}") if @config.debug_enabled
18
+ @queue.push(item)
19
+ end
20
+
21
+ def start
22
+ if @running.value
23
+ @config.logger.debug('segments worker already running.') if @config.debug_enabled
24
+ return
25
+ end
26
+
27
+ @running.make_true
28
+ perform_thread
29
+ end
30
+
31
+ def stop
32
+ unless @running.value
33
+ @config.logger.debug('segments worker not running.') if @config.debug_enabled
34
+ return
35
+ end
36
+
37
+ @running.make_false
38
+ SplitIoClient::Helpers::ThreadHelper.stop(:segment_update_worker, @config)
39
+ end
40
+
41
+ private
42
+
43
+ def perform
44
+ while (item = @queue.pop)
45
+ segment_name = item[:segment_name]
46
+ cn = item[:change_number]
47
+ @config.logger.debug("SegmentsWorker change_number dequeue #{segment_name}, #{cn}") if @config.debug_enabled
48
+
49
+ @synchronizer.fetch_segment(segment_name, cn)
50
+ end
51
+ end
52
+
53
+ def perform_thread
54
+ @config.threads[:segment_update_worker] = Thread.new do
55
+ @config.logger.debug('Starting segments worker ...') if @config.debug_enabled
56
+ perform
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module Workers
6
+ class SplitsWorker
7
+ def initialize(synchronizer, config, feature_flags_repository, telemetry_runtime_producer,
8
+ segment_fetcher, rule_based_segment_repository)
9
+ @synchronizer = synchronizer
10
+ @config = config
11
+ @feature_flags_repository = feature_flags_repository
12
+ @queue = Queue.new
13
+ @running = Concurrent::AtomicBoolean.new(false)
14
+ @telemetry_runtime_producer = telemetry_runtime_producer
15
+ @segment_fetcher = segment_fetcher
16
+ @rule_based_segment_repository = rule_based_segment_repository
17
+ end
18
+
19
+ def start
20
+ if @running.value
21
+ @config.logger.debug('feature_flags_worker already running.') if @config.debug_enabled
22
+ return
23
+ end
24
+
25
+ @running.make_true
26
+ perform_thread
27
+ end
28
+
29
+ def stop
30
+ unless @running.value
31
+ @config.logger.debug('feature_flags_worker not running.') if @config.debug_enabled
32
+ return
33
+ end
34
+
35
+ @running.make_false
36
+ Helpers::ThreadHelper.stop(:split_update_worker, @config)
37
+ end
38
+
39
+ def add_to_queue(notification)
40
+ @config.logger.debug("feature_flags_worker add to queue #{notification.data['changeNumber']}") if @config.debug_enabled
41
+ @queue.push(notification)
42
+ end
43
+
44
+ private
45
+
46
+ def perform_thread
47
+ @config.threads[:split_update_worker] = Thread.new do
48
+ @config.logger.debug('starting feature_flags_worker ...') if @config.debug_enabled
49
+ perform
50
+ end
51
+ end
52
+
53
+ def perform
54
+ while (notification = @queue.pop)
55
+ if @config.debug_enabled
56
+ @config.logger.debug("feature_flags_worker change_number dequeue #{notification.data['changeNumber']}")
57
+ end
58
+ case notification.data['type']
59
+ when SSE::EventSource::EventTypes::SPLIT_UPDATE
60
+ success = update_feature_flag(notification)
61
+ @synchronizer.fetch_splits(notification.data['changeNumber'], 0) unless success
62
+ when SSE::EventSource::EventTypes::RB_SEGMENT_UPDATE
63
+ success = update_rule_based_segment(notification)
64
+ @synchronizer.fetch_splits(0, notification.data['changeNumber']) unless success
65
+ when SSE::EventSource::EventTypes::SPLIT_KILL
66
+ kill_feature_flag(notification)
67
+ end
68
+ end
69
+ end
70
+
71
+ def update_feature_flag(notification)
72
+ return true if @feature_flags_repository.get_change_number.to_i >= notification.data['changeNumber']
73
+ return false unless !notification.data['d'].nil? && @feature_flags_repository.get_change_number == notification.data['pcn']
74
+
75
+ new_split = update_feature_flag_repository(notification)
76
+ fetch_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, 'IN_SEGMENT'), @feature_flags_repository)
77
+ if fetch_rule_based_segments_if_not_exists(Helpers::Util.segment_names_by_object(new_split, 'IN_RULE_BASED_SEGMENT'),
78
+ notification.data['changeNumber'])
79
+ return true
80
+ end
81
+
82
+ @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS)
83
+
84
+ true
85
+ rescue StandardError => e
86
+ @config.logger.debug("Failed to update Split: #{e.inspect}") if @config.debug_enabled
87
+
88
+ false
89
+ end
90
+
91
+ def update_feature_flag_repository(notification)
92
+ new_split = return_object_from_json(notification)
93
+ SplitIoClient::Helpers::RepositoryHelper.update_feature_flag_repository(@feature_flags_repository, [new_split],
94
+ notification.data['changeNumber'], @config, false)
95
+ new_split
96
+ end
97
+
98
+ def update_rule_based_segment(notification)
99
+ return true if @rule_based_segment_repository.get_change_number.to_i >= notification.data['changeNumber']
100
+ return false unless !notification.data['d'].nil? &&
101
+ @rule_based_segment_repository.get_change_number == notification.data['pcn']
102
+
103
+ new_rb_segment = return_object_from_json(notification)
104
+ SplitIoClient::Helpers::RepositoryHelper.update_rule_based_segment_repository(@rule_based_segment_repository,
105
+ [new_rb_segment],
106
+ notification.data['changeNumber'], @config)
107
+ fetch_segments_if_not_exists(Helpers::Util.segment_names_in_rb_segment(new_rb_segment, 'IN_SEGMENT'),
108
+ @rule_based_segment_repository)
109
+
110
+ # @telemetry_runtime_producer.record_updates_from_sse(Telemetry::Domain::Constants::SPLITS)
111
+
112
+ true
113
+ rescue StandardError => e
114
+ @config.logger.debug("Failed to update Split: #{e.inspect}") if @config.debug_enabled
115
+
116
+ false
117
+ end
118
+
119
+ def kill_feature_flag(notification)
120
+ return if @feature_flags_repository.get_change_number.to_i > notification.data['changeNumber']
121
+
122
+ if @config.debug_enabled
123
+ @config.logger.debug("feature_flags_worker kill #{notification.data['splitName']}, #{notification.data['changeNumber']}")
124
+ end
125
+ @feature_flags_repository.kill(notification.data['changeNumber'],
126
+ notification.data['splitName'],
127
+ notification.data['defaultTreatment'])
128
+ @synchronizer.fetch_splits(notification.data['changeNumber'], 0)
129
+ end
130
+
131
+ def return_object_from_json(notification)
132
+ object_json = Helpers::DecryptionHelper.get_encoded_definition(notification.data['c'], notification.data['d'])
133
+ JSON.parse(object_json, symbolize_names: true)
134
+ end
135
+
136
+ def fetch_segments_if_not_exists(segment_names, object_repository)
137
+ return if segment_names.nil?
138
+
139
+ object_repository.set_segment_names(segment_names)
140
+ @segment_fetcher.fetch_segments_if_not_exists(segment_names)
141
+ end
142
+
143
+ def fetch_rule_based_segments_if_not_exists(segment_names, change_number)
144
+ return false if segment_names.nil? || segment_names.empty? || @rule_based_segment_repository.contains?(segment_names.to_a)
145
+
146
+ @synchronizer.fetch_splits(0, change_number)
147
+
148
+ true
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Telemetry
5
+ module Domain
6
+ class Constants
7
+ BUR_TIMEOUT = 'bur_timeout'
8
+ NON_READY_USAGES = 'non_ready_usages'
9
+
10
+ IMPRESSIONS_DROPPED = 'impressions_dropped'
11
+ IMPRESSIONS_DEDUPE = 'impressions_deduped'
12
+ IMPRESSIONS_QUEUED = 'impressions_queued'
13
+
14
+ EVENTS_DROPPED = 'events_dropped'
15
+ EVENTS_QUEUED = 'events_queued'
16
+
17
+ SPLIT_SYNC = 'split_sync'
18
+ SEGMENT_SYNC = 'segment_sync'
19
+ IMPRESSIONS_SYNC = 'impressions_sync'
20
+ IMPRESSION_COUNT_SYNC = 'impression_count_sync'
21
+ EVENT_SYNC = 'event_sync'
22
+ TELEMETRY_SYNC = 'telemetry_sync'
23
+ TOKEN_SYNC = 'token_sync'
24
+
25
+ SSE_CONNECTION_ESTABLISHED = 0
26
+ OCCUPANCY_PRI = 10
27
+ OCCUPANCY_SEC = 20
28
+ STREAMING_STATUS = 30
29
+ CONNECTION_ERROR = 40
30
+ TOKEN_REFRESH = 50
31
+ ABLY_ERROR = 60
32
+ SYNC_MODE = 70
33
+
34
+ TREATMENT = 'treatment'
35
+ TREATMENTS = 'treatments'
36
+ TREATMENT_WITH_CONFIG = 'treatmentWithConfig'
37
+ TREATMENTS_WITH_CONFIG = 'treatmentsWithConfig'
38
+ TREATMENTS_BY_FLAG_SET = 'treatmentsByFlagSet'
39
+ TREATMENTS_BY_FLAG_SETS = 'treatmentsByFlagSets'
40
+ TREATMENTS_WITH_CONFIG_BY_FLAG_SET = 'treatmentWithConfigByFlagSet'
41
+ TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = 'treatmentsWithConfigByFlagSets'
42
+ TRACK = 'track'
43
+
44
+ SPLITS = 'splits'
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Telemetry
5
+ # sp: splits, se: segments, im: impressions, ic:impression count, ev: events, te: telemetry, to: token.
6
+ LastSynchronization = Struct.new(:sp, :se, :im, :ic, :ev, :te, :to)
7
+ HttpErrors = Struct.new(:sp, :se, :im, :ic, :ev, :te, :to)
8
+ HttpLatencies = Struct.new(:sp, :se, :im, :ic, :ev, :te, :to)
9
+
10
+ # sp: splits, se: segmentos, im: impressions, ev: events, t: telemetry
11
+ Rates = Struct.new(:sp, :se, :im, :ev, :te)
12
+
13
+ # s: sdkUrl, e: eventsUrl, a: authUrl, st: streamUrl, t: telemetryUrl
14
+ UrlOverrides = Struct.new(:s, :e, :a, :st, :t)
15
+
16
+ # om: operationMode, st: storage, af: activeFactories, rf: redundantActiveFactories, t: tags, se: streamingEnabled,
17
+ # rr: refreshRate, uo: urlOverrides, iq: impressionsQueueSize, eq: eventsQueueSize, im: impressionsMode,
18
+ # il: impressionListenerEnabled, hp: httpProxyDetected, tr: timeUntilSdkReady, bt: burTimeouts,
19
+ # nr: sdkNotReadyUsage, i: integrations, fsT: Total flagsets, fsI: Invalid flagsets
20
+ ConfigInit = Struct.new(:om, :st, :af, :rf, :t, :fsT, :fsI, :se, :rr, :uo, :iq, :eq, :im, :il, :hp, :tr, :bt, :nr, :i)
21
+
22
+ # ls: lastSynchronization, ml: clientMethodLatencies, me: clientMethodExceptions, he: httpErros, hl: httpLatencies,
23
+ # tr: tokenRefreshes, ar: authRejections, iq: impressionsQueued, ide: impressionsDeduped, idr: impressionsDropped,
24
+ # spc: splitsCount, sec: segmentCount, skc: segmentKeyCount, sl: sessionLengthMs, eq: eventsQueued, ed: eventsDropped,
25
+ # se: streamingEvents, t: tags, ufs: updates from sse
26
+ Usage = Struct.new(:ls, :ml, :me, :he, :hl, :tr, :ar, :iq, :ide, :idr, :spc, :sec, :skc, :sl, :eq, :ed, :se, :t, :ufs)
27
+
28
+ # t: treatment, ts: treatments, tc: treatmentWithConfig, tcs: treatmentsWithConfig, tr: track
29
+ ClientMethodLatencies = Struct.new(:t, :ts, :tc, :tcs, :tf, :tfs, :tcf, :tcfs, :tr)
30
+ ClientMethodExceptions = Struct.new(:t, :ts, :tc, :tcs, :tf, :tfs, :tcf, :tcfs, :tr)
31
+
32
+ # sp: splits
33
+ UpdatesFromSSE = Struct.new(:sp)
34
+ end
35
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Telemetry
5
+ class EvaluationConsumer
6
+ extend Forwardable
7
+ def_delegators :@evaluation, :pop_latencies, :pop_exceptions
8
+
9
+ def initialize(config)
10
+ @evaluation = SplitIoClient::Telemetry::MemoryEvaluationConsumer.new(config)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Telemetry
5
+ class EvaluationProducer
6
+ extend Forwardable
7
+ def_delegators :@evaluation,
8
+ :record_latency,
9
+ :record_exception
10
+
11
+ def initialize(config)
12
+ @evaluation = case config.telemetry_adapter.class.to_s
13
+ when 'SplitIoClient::Cache::Adapters::RedisAdapter'
14
+ SplitIoClient::Telemetry::RedisEvaluationProducer.new(config)
15
+ else
16
+ SplitIoClient::Telemetry::MemoryEvaluationProducer.new(config)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Telemetry
5
+ class InitConsumer
6
+ extend Forwardable
7
+ def_delegators :@init, :non_ready_usages, :bur_timeouts
8
+
9
+ def initialize(config)
10
+ @init = SplitIoClient::Telemetry::MemoryInitConsumer.new(config)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Telemetry
5
+ class InitProducer
6
+ extend Forwardable
7
+ def_delegators :@init, :record_config, :record_non_ready_usages, :record_bur_timeout
8
+
9
+ def initialize(config)
10
+ @init = case config.telemetry_adapter.class.to_s
11
+ when 'SplitIoClient::Cache::Adapters::RedisAdapter'
12
+ SplitIoClient::Telemetry::RedisInitProducer.new(config)
13
+ else
14
+ SplitIoClient::Telemetry::MemoryInitProducer.new(config)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end