splitclient-rb 7.0.3.pre.rc6-java → 7.0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fdd2c89f5ded7d31b8f923c1a4f425c16c4f1868
4
- data.tar.gz: 584a0ac3f6146d6ebc714e4f7d9522c755ee1ad7
3
+ metadata.gz: 011bc30b94b1b3d235bf7c2d549fbe0f3e9d4399
4
+ data.tar.gz: 7673857e19e18b0967a29f7594b60b101139d818
5
5
  SHA512:
6
- metadata.gz: 484c3b699e78bd9317d6c495df29767c1b68bad4966d7e801febc49c99d3a4a0ae88c47339f109dada8a4de27557f472c949360f923c31f3346b4e3ebea78e79
7
- data.tar.gz: 3d983ce1750d405445e49efb47c4fedc4234f719d5deae7f9e175c7f765a9397785614c55507340236930b5a5c722034741ffe15df651b041654544eccf5696c
6
+ metadata.gz: 91ebecabb11b4555f8e469ecacfb5959e19cf9811a1504f8f2bd60c482531346064abf022b91919c374bb612da824d125a50c585201e774f48141b8575236778
7
+ data.tar.gz: ddfcdf53cc0d4ff4965578a01a600a444d514598c07afaad3d67001b5da88dc36564653229d88e2a356277c51211ab2fdb5d182e653e754c68589c790b976194
@@ -1,11 +1,19 @@
1
1
  Documentation:
2
2
  Enabled: false
3
3
 
4
+ Metrics/AbcSize:
5
+ Max: 25
6
+
4
7
  Metrics/MethodLength:
5
- Max: 15
8
+ Max: 20
9
+
10
+ Metrics/ClassLength:
11
+ Max: 120
6
12
 
7
13
  Metrics/LineLength:
8
14
  Max: 121
15
+ Exclude:
16
+ - spec/sse/**/*
9
17
 
10
18
  Metrics/BlockLength:
11
19
  Exclude:
@@ -1,4 +1,4 @@
1
- 7.0.3 (Dec 6, 2019)
1
+ 7.0.3 (Jan 20, 2020)
2
2
  - Added integration tests.
3
3
  - Fixed impressions labels.
4
4
 
@@ -40,7 +40,7 @@
40
40
  define configurations for your treatments and also whitelisted keys. Read more in our docs!
41
41
 
42
42
  6.2.0 (Mar 7th, 2019)
43
- - Reworked SplitClient#destroy to ensure events, impressions and metrics are sent to Split backend when called
43
+ - Reworked SplitClient#destroy to ensure events, impressions and metrics are sent to Split backend when called.
44
44
  - Ensured destroy is called when keyboard interrupts are sent to the application
45
45
  - Changed SDK blocker (and block_until_ready) to have no effect in consumer mode
46
46
  - Added support for applications tied to Faraday < 0.13 and net-http-persistent 3 using a patched Faraday adapter
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2018 Split Software, Co.
1
+ Copyright © 2020 Split Software, Inc.
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
@@ -9,6 +9,8 @@ require 'splitclient-rb/cache/adapters/memory_adapters/queue_adapter'
9
9
  require 'splitclient-rb/cache/adapters/cache_adapter'
10
10
  require 'splitclient-rb/cache/adapters/memory_adapter'
11
11
  require 'splitclient-rb/cache/adapters/redis_adapter'
12
+ require 'splitclient-rb/cache/fetchers/segment_fetcher'
13
+ require 'splitclient-rb/cache/fetchers/split_fetcher'
12
14
  require 'splitclient-rb/cache/repositories/repository'
13
15
  require 'splitclient-rb/cache/repositories/segments_repository'
14
16
  require 'splitclient-rb/cache/repositories/splits_repository'
@@ -29,8 +31,6 @@ require 'splitclient-rb/cache/senders/localhost_repo_cleaner'
29
31
  require 'splitclient-rb/cache/stores/store_utils'
30
32
  require 'splitclient-rb/cache/stores/localhost_split_builder'
31
33
  require 'splitclient-rb/cache/stores/sdk_blocker'
32
- require 'splitclient-rb/cache/stores/segment_store'
33
- require 'splitclient-rb/cache/stores/split_store'
34
34
  require 'splitclient-rb/cache/stores/localhost_split_store'
35
35
 
36
36
  require 'splitclient-rb/clients/split_client'
@@ -88,6 +88,15 @@ require 'splitclient-rb/utilitites'
88
88
  # redis metrics fixer
89
89
  require 'splitclient-rb/redis_metrics_fixer'
90
90
 
91
+ # SSE
92
+ require 'splitclient-rb/sse/event_source/client'
93
+ require 'splitclient-rb/sse/event_source/event_types'
94
+ require 'splitclient-rb/sse/event_source/status'
95
+ require 'splitclient-rb/sse/workers/control_worker'
96
+ require 'splitclient-rb/sse/workers/segments_worker'
97
+ require 'splitclient-rb/sse/workers/splits_worker'
98
+ require 'splitclient-rb/sse/sse_handler'
99
+
91
100
  # C extension
92
101
  require 'murmurhash/murmurhash_mri'
93
102
 
@@ -1,7 +1,7 @@
1
1
  module SplitIoClient
2
2
  module Cache
3
- module Stores
4
- class SegmentStore
3
+ module Fetchers
4
+ class SegmentFetcher
5
5
  attr_reader :segments_repository
6
6
 
7
7
  def initialize(segments_repository, api_key, metrics, config, sdk_blocker = nil)
@@ -14,7 +14,7 @@ module SplitIoClient
14
14
 
15
15
  def call
16
16
  if ENV['SPLITCLIENT_ENV'] == 'test'
17
- store_segments
17
+ fetch_segments
18
18
  else
19
19
  segments_thread
20
20
 
@@ -26,16 +26,30 @@ module SplitIoClient
26
26
  end
27
27
  end
28
28
 
29
+ def fetch_segment(name)
30
+ segments_api.fetch_segments_by_names([name])
31
+ rescue StandardError => error
32
+ @config.log_found_exception(__method__.to_s, error)
33
+ end
34
+
35
+ def fetch_segments
36
+ segments_api.fetch_segments_by_names(@segments_repository.used_segment_names)
37
+
38
+ @sdk_blocker.segments_ready!
39
+ rescue StandardError => error
40
+ @config.log_found_exception(__method__.to_s, error)
41
+ end
42
+
29
43
  private
30
44
 
31
45
  def segments_thread
32
- @config.threads[:segment_store] = Thread.new do
46
+ @config.threads[:segment_fetcher] = Thread.new do
33
47
  @config.logger.info('Starting segments fetcher service')
34
48
 
35
49
  loop do
36
50
  next unless @sdk_blocker.splits_repository.ready?
37
51
 
38
- store_segments
52
+ fetch_segments
39
53
  @config.logger.debug("Segment names: #{@segments_repository.used_segment_names.to_a}") if @config.debug_enabled
40
54
 
41
55
  sleep_for = StoreUtils.random_interval(@config.segments_refresh_rate)
@@ -43,15 +57,7 @@ module SplitIoClient
43
57
  sleep(sleep_for)
44
58
  end
45
59
  end
46
- end
47
-
48
- def store_segments
49
- segments_api.store_segments_by_names(@segments_repository.used_segment_names)
50
-
51
- @sdk_blocker.segments_ready!
52
- rescue StandardError => error
53
- @config.log_found_exception(__method__.to_s, error)
54
- end
60
+ end
55
61
 
56
62
  def segments_api
57
63
  @segments_api ||= SplitIoClient::Api::Segments.new(@api_key, @metrics, @segments_repository, @config)
@@ -1,7 +1,7 @@
1
1
  module SplitIoClient
2
2
  module Cache
3
- module Stores
4
- class SplitStore
3
+ module Fetchers
4
+ class SplitFetcher
5
5
  attr_reader :splits_repository
6
6
 
7
7
  def initialize(splits_repository, api_key, metrics, config, sdk_blocker = nil)
@@ -14,7 +14,7 @@ module SplitIoClient
14
14
 
15
15
  def call
16
16
  if ENV['SPLITCLIENT_ENV'] == 'test'
17
- store_splits
17
+ fetch_splits
18
18
  else
19
19
  splits_thread
20
20
 
@@ -26,20 +26,7 @@ module SplitIoClient
26
26
  end
27
27
  end
28
28
 
29
- private
30
-
31
- def splits_thread
32
- @config.threads[:split_store] = Thread.new do
33
- @config.logger.info('Starting splits fetcher service')
34
- loop do
35
- store_splits
36
-
37
- sleep(StoreUtils.random_interval(@config.features_refresh_rate))
38
- end
39
- end
40
- end
41
-
42
- def store_splits
29
+ def fetch_splits
43
30
  data = splits_since(@splits_repository.get_change_number)
44
31
 
45
32
  data[:splits] && data[:splits].each do |split|
@@ -57,6 +44,19 @@ module SplitIoClient
57
44
  @config.log_found_exception(__method__.to_s, error)
58
45
  end
59
46
 
47
+ private
48
+
49
+ def splits_thread
50
+ @config.threads[:split_fetcher] = Thread.new do
51
+ @config.logger.info('Starting splits fetcher service')
52
+ loop do
53
+ fetch_splits
54
+
55
+ sleep(StoreUtils.random_interval(@config.features_refresh_rate))
56
+ end
57
+ end
58
+ end
59
+
60
60
  def splits_since(since)
61
61
  splits_api.since(since)
62
62
  end
@@ -83,8 +83,6 @@ module SplitIoClient
83
83
  @splits_repository.add_split(split)
84
84
  end
85
85
 
86
- private
87
-
88
86
  def splits_api
89
87
  @splits_api ||= SplitIoClient::Api::Splits.new(@api_key, @metrics, @config)
90
88
  end
@@ -128,6 +128,18 @@ module SplitIoClient
128
128
  @adapter.clear(namespace_key)
129
129
  end
130
130
 
131
+ def kill(change_number, split_name, default_treatment)
132
+ split = get_split(split_name)
133
+
134
+ return if split.nil?
135
+
136
+ split[:label] = Engine::Models::Label::KILLED
137
+ split[:defaultTreatment] = default_treatment
138
+ split[:changeNumber] = change_number
139
+
140
+ @adapter.set_string(namespace_key(".split.#{split_name}"), split.to_json)
141
+ end
142
+
131
143
  private
132
144
 
133
145
  def increase_tt_name_count(tt_name)
@@ -9,7 +9,7 @@ module SplitIoClient
9
9
  # @param api_key [String] the API key for your split account
10
10
  #
11
11
  # @return [SplitIoClient] split.io client instance
12
- def initialize(api_key, adapter = nil, splits_repository, segments_repository, impressions_repository, metrics_repository, events_repository, sdk_blocker, config)
12
+ def initialize(api_key, adapter = nil, splits_repository, segments_repository, impressions_repository, metrics_repository, events_repository, sdk_blocker, config, sse_handler)
13
13
  @api_key = api_key
14
14
  @splits_repository = splits_repository
15
15
  @segments_repository = segments_repository
@@ -20,6 +20,7 @@ module SplitIoClient
20
20
  @destroyed = false
21
21
  @config = config
22
22
  @adapter = adapter
23
+ @sse_handler = sse_handler
23
24
  end
24
25
 
25
26
  def get_treatment(
@@ -61,6 +62,8 @@ module SplitIoClient
61
62
  thread.join
62
63
  end
63
64
 
65
+ @sse_handler.sse_client.close if @config.push_notification_enabled
66
+
64
67
  @config.threads.values.each { |thread| Thread.kill(thread) }
65
68
 
66
69
  @splits_repository.clear
@@ -13,7 +13,7 @@ module SplitIoClient
13
13
  @segments_repository = segments_repository
14
14
  end
15
15
 
16
- def store_segments_by_names(names)
16
+ def fetch_segments_by_names(names)
17
17
  start = Time.now
18
18
 
19
19
  return if names.nil? || names.empty?
@@ -1,6 +1,7 @@
1
1
  require 'json'
2
2
  require 'thread'
3
3
 
4
+ include SplitIoClient::Cache::Fetchers
4
5
  include SplitIoClient::Cache::Stores
5
6
  include SplitIoClient::Cache::Senders
6
7
 
@@ -11,7 +12,7 @@ module SplitIoClient
11
12
  # also, uses safe threads to execute fetches and post give the time execution values from the config
12
13
  #
13
14
  class SplitAdapter < NoMethodError
14
- attr_reader :splits_repository, :segments_repository, :impressions_repository, :metrics
15
+ attr_reader :splits_repository, :segments_repository, :impressions_repository, :metrics, :split_fetcher, :segment_fetcher
15
16
 
16
17
  #
17
18
  # Creates a new split api adapter instance that consumes split api endpoints
@@ -50,8 +51,8 @@ module SplitIoClient
50
51
  end
51
52
 
52
53
  def start_standalone_components
53
- split_store
54
- segment_store
54
+ split_fetch
55
+ segment_fetch
55
56
  metrics_sender
56
57
  impressions_sender
57
58
  events_sender
@@ -73,13 +74,15 @@ module SplitIoClient
73
74
  end
74
75
 
75
76
  # Starts thread which loops constantly and stores splits in the splits_repository of choice
76
- def split_store
77
- SplitStore.new(@splits_repository, @api_key, @metrics, @config, @sdk_blocker).call
77
+ def split_fetch
78
+ @split_fetcher = SplitFetcher.new(@splits_repository, @api_key, @metrics, @config, @sdk_blocker)
79
+ @split_fetcher.fetch_splits
78
80
  end
79
81
 
80
82
  # Starts thread which loops constantly and stores segments in the segments_repository of choice
81
- def segment_store
82
- SegmentStore.new(@segments_repository, @api_key, @metrics, @config, @sdk_blocker).call
83
+ def segment_fetch
84
+ @segment_fetcher = SegmentFetcher.new(@segments_repository, @api_key, @metrics, @config, @sdk_blocker)
85
+ @segment_fetcher.fetch_segments
83
86
  end
84
87
 
85
88
  # Starts thread which loops constantly and sends impressions to the Split API
@@ -102,6 +102,8 @@ module SplitIoClient
102
102
  @split_validator = SplitIoClient::Validators.new(self)
103
103
  @localhost_mode = opts[:localhost_mode]
104
104
 
105
+ @push_notification_enabled = opts[:push_notification_enabled].nil? ? true : false
106
+
105
107
  startup_log
106
108
  end
107
109
 
@@ -172,7 +174,6 @@ module SplitIoClient
172
174
  # @return [SplitLogger] The configured logger
173
175
  attr_accessor :split_logger
174
176
 
175
-
176
177
  #
177
178
  # The split validator. The client library uses the split validator
178
179
  # to validate inputs accross the sdk
@@ -255,6 +256,8 @@ module SplitIoClient
255
256
 
256
257
  attr_accessor :ip_addresses_enabled
257
258
 
259
+ attr_accessor :push_notification_enabled
260
+
258
261
  #
259
262
  # The default split client configuration
260
263
  #
@@ -36,7 +36,9 @@ module SplitIoClient
36
36
 
37
37
  @adapter = start!
38
38
 
39
- @client = SplitClient.new(@api_key, @adapter, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config)
39
+ start_sse!
40
+
41
+ @client = SplitClient.new(@api_key, @adapter, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config, @sse_handler)
40
42
  @manager = SplitManager.new(@splits_repository, @sdk_blocker, @config)
41
43
 
42
44
  validate_api_key
@@ -115,5 +117,16 @@ module SplitIoClient
115
117
  @config.valid_mode = false
116
118
  end
117
119
  end
120
+
121
+ def start_sse!
122
+ if @config.push_notification_enabled
123
+ @splits_worker = SSE::Workers::SplitsWorker.new(@adapter, @config, @splits_repository)
124
+ @segments_worker = SSE::Workers::SegmentsWorker.new(@adapter, @config, @segments_repository)
125
+ @control_worker = SSE::Workers::ControlWorker.new(@adapter, @config)
126
+
127
+ options = { channels: 'mauro-c', key: 'fake_key', url_host: 'fake-url' }
128
+ @sse_handler = SSE::SSEHandler.new(@config, options, @splits_worker, @segments_worker, @control_worker)
129
+ end
130
+ end
118
131
  end
119
132
  end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'concurrent/atomics'
4
+ require 'socketry'
5
+ require 'uri'
6
+
7
+ module SplitIoClient
8
+ module SSE
9
+ module EventSource
10
+ class Client
11
+ DEFAULT_READ_TIMEOUT = 200
12
+ KEEP_ALIVE_RESPONSE = "c\r\n:keepalive\n\n\r\n".freeze
13
+
14
+ def initialize(url, config, read_timeout: DEFAULT_READ_TIMEOUT)
15
+ @uri = URI(url)
16
+ @config = config
17
+ @read_timeout = read_timeout
18
+ @connected = Concurrent::AtomicBoolean.new(false)
19
+ @socket = nil
20
+
21
+ @on = { event: ->(_) {}, error: ->(_) {} }
22
+
23
+ yield self if block_given?
24
+
25
+ connect_thread
26
+
27
+ connect_passenger_forked if defined?(PhusionPassenger)
28
+ end
29
+
30
+ def on_event(&action)
31
+ @on[:event] = action
32
+ end
33
+
34
+ def on_error(&action)
35
+ @on[:error] = action
36
+ end
37
+
38
+ def close
39
+ @connected.make_false
40
+ @socket&.close
41
+ @socket = nil
42
+ end
43
+
44
+ def status
45
+ return Status::CONNECTED if @connected.value
46
+
47
+ Status::DISCONNECTED
48
+ end
49
+
50
+ private
51
+
52
+ def connect_thread
53
+ @config.threads[:connect_stream] = Thread.new do
54
+ connect_stream
55
+ end
56
+ end
57
+
58
+ def connect_passenger_forked
59
+ PhusionPassenger.on_event(:starting_worker_process) { |forked| connect_thread if forked }
60
+ end
61
+
62
+ def connect_stream
63
+ @config.logger.info("Connecting to #{@uri.host}...")
64
+
65
+ begin
66
+ @socket = socket_connect
67
+
68
+ @socket.write(build_request(@uri))
69
+ @connected.make_true
70
+ rescue StandardError => e
71
+ dispatch_error(e.inspect)
72
+ end
73
+
74
+ while @connected.value
75
+ begin
76
+ partial_data = @socket.readpartial(2048, timeout: @read_timeout)
77
+ rescue Socketry::TimeoutError
78
+ @config.logger.error("Socket read time out in #{@read_timeout}")
79
+ @connected.make_false
80
+ connect_stream
81
+ end
82
+
83
+ proccess_data(partial_data) unless partial_data == KEEP_ALIVE_RESPONSE
84
+ end
85
+ end
86
+
87
+ def socket_connect
88
+ return Socketry::SSL::Socket.connect(@uri.host, @uri.port) if @uri.scheme.casecmp('https').zero?
89
+
90
+ Socketry::TCP::Socket.connect(@uri.host, @uri.port)
91
+ end
92
+
93
+ def proccess_data(partial_data)
94
+ unless partial_data.nil?
95
+ @config.logger.debug("Event partial data: #{partial_data}")
96
+ buffer = read_partial_data(partial_data)
97
+ event = parse_event(buffer)
98
+
99
+ dispatch_event(event)
100
+ end
101
+ rescue StandardError => e
102
+ dispatch_error(e.inspect)
103
+ end
104
+
105
+ def build_request(uri)
106
+ req = "GET #{uri.request_uri} HTTP/1.1\r\n"
107
+ req << "Host: #{uri.host}\r\n"
108
+ req << "Accept: text/event-stream\r\n"
109
+ req << "Cache-Control: no-cache\r\n"
110
+ req << "\r\n"
111
+ @config.logger.debug("Request info: #{req}")
112
+ req
113
+ end
114
+
115
+ def read_partial_data(data)
116
+ buffer = ''
117
+ buffer << data
118
+ buffer.chomp!
119
+ buffer.split("\n")
120
+ end
121
+
122
+ def parse_event(buffer)
123
+ event_type = nil
124
+ parsed_data = nil
125
+ client_id = nil
126
+
127
+ buffer.each do |d|
128
+ splited_data = d.split(':')
129
+
130
+ case splited_data[0]
131
+ when 'event'
132
+ event_type = splited_data[1].strip
133
+ when 'data'
134
+ event_data = JSON.parse(d.sub('data: ', ''))
135
+ client_id = event_data['clientId']&.strip
136
+ parsed_data = JSON.parse(event_data['data'])
137
+ end
138
+ end
139
+
140
+ return StreamData.new(event_type, client_id, parsed_data) unless event_type.nil? || parsed_data.nil?
141
+
142
+ raise 'Invalid event format.'
143
+ rescue StandardError => e
144
+ dispatch_error(e.inspect)
145
+ nil
146
+ end
147
+
148
+ def dispatch_event(event)
149
+ @config.logger.debug("Dispatching event: #{event}") unless event.nil?
150
+ @on[:event].call(event) unless event.nil?
151
+ end
152
+
153
+ def dispatch_error(error)
154
+ @config.logger.debug("Dispatching error: #{error}")
155
+ @on[:error].call(error)
156
+ end
157
+ end
158
+
159
+ StreamData = Struct.new(:event_type, :client_id, :data)
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,14 @@
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
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module EventSource
6
+ class Status
7
+ CONNECTING = 'Connecting'
8
+ CONNECTED = 'Connected'
9
+ DISCONNECTED = 'Disconnected'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,83 @@
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, options, splits_worker, segments_worker, control_worker)
9
+ @config = config
10
+ @options = options
11
+ @splits_worker = splits_worker
12
+ @segments_worker = segments_worker
13
+ @control_worker = control_worker
14
+
15
+ @sse_client = start_sse_client
16
+ end
17
+
18
+ private
19
+
20
+ def start_sse_client
21
+ url = "#{@options[:url_host]}/event-stream?channels=#{@options[:channels]}&v=1.1&key=#{@options[:key]}"
22
+
23
+ sse_client = SSE::EventSource::Client.new(url, @config) do |client|
24
+ client.on_event do |event|
25
+ process_event(event)
26
+ end
27
+
28
+ client.on_error do |error|
29
+ process_error(error)
30
+ end
31
+ end
32
+
33
+ sse_client
34
+ end
35
+
36
+ def process_event(event)
37
+ case event.data['type']
38
+ when SSE::EventSource::EventTypes::SPLIT_UPDATE
39
+ split_update_notification(event)
40
+ when SSE::EventSource::EventTypes::SPLIT_KILL
41
+ split_kill_notification(event)
42
+ when SSE::EventSource::EventTypes::SEGMENT_UPDATE
43
+ segment_update_notification(event)
44
+ when SSE::EventSource::EventTypes::CONTROL
45
+ control_notification(event)
46
+ else
47
+ @config.logger.error("Incorrect event type: #{event}")
48
+ end
49
+ end
50
+
51
+ def process_error(error)
52
+ @config.logger.error("SSE::EventSource::Client error: #{error}")
53
+ end
54
+
55
+ def split_update_notification(event)
56
+ @config.logger.debug("SPLIT UPDATE notification received: #{event}")
57
+ @splits_worker.add_to_queue(event.data['changeNumber'])
58
+ end
59
+
60
+ def split_kill_notification(event)
61
+ @config.logger.debug("SPLIT KILL notification received: #{event}")
62
+
63
+ change_number = event.data['changeNumber']
64
+ default_treatment = event.data['defaultTreatment']
65
+ split_name = event.data['splitName']
66
+
67
+ @splits_worker.kill_split(change_number, split_name, default_treatment)
68
+ end
69
+
70
+ def segment_update_notification(event)
71
+ @config.logger.debug("SEGMENT UPDATE notification received: #{event}")
72
+ change_number = event.data['changeNumber']
73
+ segment_name = event.data['segmentName']
74
+
75
+ @segments_worker.add_to_queue(change_number, segment_name)
76
+ end
77
+
78
+ def control_notification(event)
79
+ @config.logger.debug("CONTROL notification received: #{event}")
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module Workers
6
+ class ControlWorker
7
+ def initialize(adapter, config)
8
+ @adapter = adapter
9
+ @config = config
10
+
11
+ perform_thread
12
+
13
+ perform_passenger_forked if defined?(PhusionPassenger)
14
+ end
15
+
16
+ private
17
+
18
+ def perform
19
+ # TODO: IMPLEMENT THIS METHOD
20
+ end
21
+
22
+ def perform_thread
23
+ @config.threads[:segment_update_worker] = Thread.new do
24
+ perform
25
+ end
26
+ end
27
+
28
+ def perform_passenger_forked
29
+ PhusionPassenger.on_event(:starting_worker_process) { |forked| perform_thread if forked }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module Workers
6
+ class SegmentsWorker
7
+ def initialize(adapter, config, segments_repository)
8
+ @adapter = adapter
9
+ @config = config
10
+ @segments_repository = segments_repository
11
+ @queue = Queue.new
12
+
13
+ perform_thread
14
+
15
+ perform_passenger_forked if defined?(PhusionPassenger)
16
+ end
17
+
18
+ def add_to_queue(change_number, segment_name)
19
+ item = { change_number: change_number, segment_name: segment_name }
20
+ @queue.push(item)
21
+ end
22
+
23
+ private
24
+
25
+ def perform
26
+ while (item = @queue.pop)
27
+ segment_name = item[:segment_name]
28
+ change_number = item[:change_number]
29
+ since = @segments_repository.get_change_number(segment_name)
30
+
31
+ @adapter.segment_fetcher.fetch_segment(segment_name) unless since >= change_number
32
+ end
33
+ end
34
+
35
+ def perform_thread
36
+ @config.threads[:segment_update_worker] = Thread.new do
37
+ perform
38
+ end
39
+ end
40
+
41
+ def perform_passenger_forked
42
+ PhusionPassenger.on_event(:starting_worker_process) { |forked| perform_thread if forked }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module SSE
5
+ module Workers
6
+ class SplitsWorker
7
+ def initialize(adapter, config, splits_repository)
8
+ @adapter = adapter
9
+ @config = config
10
+ @splits_repository = splits_repository
11
+ @queue = Queue.new
12
+
13
+ perform_thread
14
+
15
+ perform_passenger_forked if defined?(PhusionPassenger)
16
+ end
17
+
18
+ def add_to_queue(change_number)
19
+ @queue.push(change_number)
20
+ end
21
+
22
+ def kill_split(change_number, split_name, default_treatment)
23
+ @splits_repository.kill(change_number, split_name, default_treatment)
24
+ add_to_queue(change_number)
25
+ end
26
+
27
+ private
28
+
29
+ def perform
30
+ while (change_number = @queue.pop)
31
+ since = @splits_repository.get_change_number
32
+ @adapter.split_fetcher.fetch_splits unless since >= change_number
33
+ end
34
+ end
35
+
36
+ def perform_thread
37
+ @config.threads[:split_update_worker] = Thread.new do
38
+ perform
39
+ end
40
+ end
41
+
42
+ def perform_passenger_forked
43
+ PhusionPassenger.on_event(:starting_worker_process) { |forked| perform_thread if forked }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '7.0.3.pre.rc6'
2
+ VERSION = '7.0.4.pre.rc1'
3
3
  end
@@ -54,5 +54,6 @@ Gem::Specification.new do |spec|
54
54
  spec.add_runtime_dependency 'lru_redux'
55
55
  spec.add_runtime_dependency 'net-http-persistent', '>= 2.9'
56
56
  spec.add_runtime_dependency 'redis', '>= 3.2'
57
+ spec.add_runtime_dependency 'socketry', '~> 0.5.1'
57
58
  spec.add_runtime_dependency 'thread_safe', '>= 0.3'
58
59
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: splitclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.3.pre.rc6
4
+ version: 7.0.4.pre.rc1
5
5
  platform: java
6
6
  authors:
7
7
  - Split Software
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-16 00:00:00.000000000 Z
11
+ date: 2020-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -262,6 +262,20 @@ dependencies:
262
262
  - - ">="
263
263
  - !ruby/object:Gem::Version
264
264
  version: '3.2'
265
+ - !ruby/object:Gem::Dependency
266
+ requirement: !ruby/object:Gem::Requirement
267
+ requirements:
268
+ - - "~>"
269
+ - !ruby/object:Gem::Version
270
+ version: 0.5.1
271
+ name: socketry
272
+ prerelease: false
273
+ type: :runtime
274
+ version_requirements: !ruby/object:Gem::Requirement
275
+ requirements:
276
+ - - "~>"
277
+ - !ruby/object:Gem::Version
278
+ version: 0.5.1
265
279
  - !ruby/object:Gem::Dependency
266
280
  requirement: !ruby/object:Gem::Requirement
267
281
  requirements:
@@ -307,6 +321,8 @@ files:
307
321
  - lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb
308
322
  - lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb
309
323
  - lib/splitclient-rb/cache/adapters/redis_adapter.rb
324
+ - lib/splitclient-rb/cache/fetchers/segment_fetcher.rb
325
+ - lib/splitclient-rb/cache/fetchers/split_fetcher.rb
310
326
  - lib/splitclient-rb/cache/repositories/events/memory_repository.rb
311
327
  - lib/splitclient-rb/cache/repositories/events/redis_repository.rb
312
328
  - lib/splitclient-rb/cache/repositories/events_repository.rb
@@ -328,8 +344,6 @@ files:
328
344
  - lib/splitclient-rb/cache/stores/localhost_split_builder.rb
329
345
  - lib/splitclient-rb/cache/stores/localhost_split_store.rb
330
346
  - lib/splitclient-rb/cache/stores/sdk_blocker.rb
331
- - lib/splitclient-rb/cache/stores/segment_store.rb
332
- - lib/splitclient-rb/cache/stores/split_store.rb
333
347
  - lib/splitclient-rb/cache/stores/store_utils.rb
334
348
  - lib/splitclient-rb/clients/split_client.rb
335
349
  - lib/splitclient-rb/engine/api/client.rb
@@ -380,6 +394,13 @@ files:
380
394
  - lib/splitclient-rb/split_factory_builder.rb
381
395
  - lib/splitclient-rb/split_factory_registry.rb
382
396
  - lib/splitclient-rb/split_logger.rb
397
+ - lib/splitclient-rb/sse/event_source/client.rb
398
+ - lib/splitclient-rb/sse/event_source/event_types.rb
399
+ - lib/splitclient-rb/sse/event_source/status.rb
400
+ - lib/splitclient-rb/sse/sse_handler.rb
401
+ - lib/splitclient-rb/sse/workers/control_worker.rb
402
+ - lib/splitclient-rb/sse/workers/segments_worker.rb
403
+ - lib/splitclient-rb/sse/workers/splits_worker.rb
383
404
  - lib/splitclient-rb/utilitites.rb
384
405
  - lib/splitclient-rb/validators.rb
385
406
  - lib/splitclient-rb/version.rb