splitclient-rb 7.1.4.pre.rc19-java → 7.2.2.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 +4 -4
- data/CHANGES.txt +12 -0
- data/lib/splitclient-rb.rb +1 -0
- data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +1 -1
- data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +21 -21
- data/lib/splitclient-rb/engine/push_manager.rb +7 -5
- data/lib/splitclient-rb/engine/sync_manager.rb +33 -13
- data/lib/splitclient-rb/engine/synchronizer.rb +5 -3
- data/lib/splitclient-rb/split_factory.rb +14 -10
- data/lib/splitclient-rb/sse/event_source/client.rb +42 -65
- data/lib/splitclient-rb/sse/event_source/event_parser.rb +55 -0
- data/lib/splitclient-rb/sse/sse_handler.rb +3 -3
- data/lib/splitclient-rb/sse/workers/segments_worker.rb +21 -9
- data/lib/splitclient-rb/sse/workers/splits_worker.rb +27 -9
- data/lib/splitclient-rb/version.rb +1 -1
- data/splitclient-rb.gemspec +1 -1
- metadata +5 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e603a0d1043dbb6dcc4343222bbd8944754d94fe
         | 
| 4 | 
            +
              data.tar.gz: 77b884a85892122480540e4a1bca4ae9ebc6bbd2
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: af1317c7dccbdafc9eef1743fac441c61cde55abac06cebad30c5a99bd39da11b3ef505f12ebe3c2532135ba35baadef4b2f96449542c2a5d19b4db0af8dec1e
         | 
| 7 | 
            +
              data.tar.gz: 99a3568b184c7e3cafab887fe9bdd2c31aff1a039f36904fc0d80a0aa03b51c67e73b215dd013ab3c49bd7c3f00a6c68cc160e6d60d4dbb06115524f430be46b
         | 
    
        data/CHANGES.txt
    CHANGED
    
    | @@ -1,5 +1,17 @@ | |
| 1 1 | 
             
            CHANGES
         | 
| 2 2 |  | 
| 3 | 
            +
            7.2.2 (Dec 18, 2020)
         | 
| 4 | 
            +
            - Fixed issue: undefined local variable or method post_impressions_count
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            7.2.1 (Oct 23, 2020)
         | 
| 7 | 
            +
            - Updated redis dependency to >= 4.2.2.
         | 
| 8 | 
            +
            - Updated ably error handling. 
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            7.2.0 (Sep 25, 2020)
         | 
| 11 | 
            +
            - Added deduplication logic for impressions data.
         | 
| 12 | 
            +
              - Now there are two modes for Impressions when the SDK is in standalone mode, OPTIMIZED (default) that only ships unique impressions and DEBUG for times where you need to send ALL impressions to debug an integration.
         | 
| 13 | 
            +
              - Impression listener remains unchanged and will still get all impressions.
         | 
| 14 | 
            +
             | 
| 3 15 | 
             
            7.1.3 (Jul 31, 2020)
         | 
| 4 16 | 
             
            - Updated rake development dependency to ~> 12.3.3.
         | 
| 5 17 |  | 
    
        data/lib/splitclient-rb.rb
    CHANGED
    
    | @@ -101,6 +101,7 @@ require 'splitclient-rb/redis_metrics_fixer' | |
| 101 101 | 
             
            # SSE 
         | 
| 102 102 | 
             
            require 'splitclient-rb/sse/event_source/back_off'
         | 
| 103 103 | 
             
            require 'splitclient-rb/sse/event_source/client'
         | 
| 104 | 
            +
            require 'splitclient-rb/sse/event_source/event_parser'
         | 
| 104 105 | 
             
            require 'splitclient-rb/sse/event_source/event_types'
         | 
| 105 106 | 
             
            require 'splitclient-rb/sse/event_source/stream_data'
         | 
| 106 107 | 
             
            require 'splitclient-rb/sse/workers/segments_worker'
         | 
| @@ -40,32 +40,32 @@ module SplitIoClient | |
| 40 40 | 
             
                          @config.logger.info('Posting impressions count due to shutdown')
         | 
| 41 41 | 
             
                        end
         | 
| 42 42 | 
             
                      end
         | 
| 43 | 
            +
                    end
         | 
| 43 44 |  | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
                      def formatter(counts)
         | 
| 51 | 
            -
                        return if counts.empty?
         | 
| 45 | 
            +
                    def post_impressions_count
         | 
| 46 | 
            +
                      @impressions_api.post_count(formatter(@impression_counter.pop_all))
         | 
| 47 | 
            +
                    rescue StandardError => error
         | 
| 48 | 
            +
                      @config.log_found_exception(__method__.to_s, error)
         | 
| 49 | 
            +
                    end
         | 
| 52 50 |  | 
| 53 | 
            -
             | 
| 51 | 
            +
                    def formatter(counts)
         | 
| 52 | 
            +
                      return if counts.empty?
         | 
| 54 53 |  | 
| 55 | 
            -
             | 
| 56 | 
            -
                          key_splited = key.split('::')
         | 
| 57 | 
            -
                          
         | 
| 58 | 
            -
                          formated_counts[:pf] << {
         | 
| 59 | 
            -
                            f: key_splited[0].to_s, # feature name
         | 
| 60 | 
            -
                            m: key_splited[1].to_i, # time frame
         | 
| 61 | 
            -
                            rc: value # count
         | 
| 62 | 
            -
                          }
         | 
| 63 | 
            -
                        end
         | 
| 54 | 
            +
                      formated_counts = {pf: []}
         | 
| 64 55 |  | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
                         | 
| 56 | 
            +
                      counts.each do |key, value|              
         | 
| 57 | 
            +
                        key_splited = key.split('::')
         | 
| 58 | 
            +
                        
         | 
| 59 | 
            +
                        formated_counts[:pf] << {
         | 
| 60 | 
            +
                          f: key_splited[0].to_s, # feature name
         | 
| 61 | 
            +
                          m: key_splited[1].to_i, # time frame
         | 
| 62 | 
            +
                          rc: value # count
         | 
| 63 | 
            +
                        }
         | 
| 68 64 | 
             
                      end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      formated_counts
         | 
| 67 | 
            +
                    rescue StandardError => error
         | 
| 68 | 
            +
                      @config.log_found_exception(__method__.to_s, error)
         | 
| 69 69 | 
             
                    end
         | 
| 70 70 | 
             
                  end
         | 
| 71 71 | 
             
                end
         | 
| @@ -15,14 +15,15 @@ module SplitIoClient | |
| 15 15 | 
             
                    response = @auth_api_client.authenticate(@api_key)
         | 
| 16 16 |  | 
| 17 17 | 
             
                    @config.logger.debug("Auth service response push_enabled: #{response[:push_enabled]}") if @config.debug_enabled
         | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 18 | 
            +
             | 
| 19 | 
            +
                    if response[:push_enabled] && @sse_handler.start(response[:token], response[:channels])
         | 
| 20 20 | 
             
                      schedule_next_token_refresh(response[:exp])
         | 
| 21 21 | 
             
                      @back_off.reset
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                      stop_sse
         | 
| 22 | 
            +
                      return
         | 
| 24 23 | 
             
                    end
         | 
| 25 24 |  | 
| 25 | 
            +
                    stop_sse
         | 
| 26 | 
            +
             | 
| 26 27 | 
             
                    schedule_next_token_refresh(@back_off.interval) if response[:retry]
         | 
| 27 28 | 
             
                  rescue StandardError => e
         | 
| 28 29 | 
             
                    @config.logger.error("start_sse: #{e.inspect}")
         | 
| @@ -31,6 +32,7 @@ module SplitIoClient | |
| 31 32 | 
             
                  def stop_sse
         | 
| 32 33 | 
             
                    @sse_handler.process_disconnect if @sse_handler.sse_client.nil?
         | 
| 33 34 | 
             
                    @sse_handler.stop
         | 
| 35 | 
            +
                    SplitIoClient::Helpers::ThreadHelper.stop(:schedule_next_token_refresh, @config)
         | 
| 34 36 | 
             
                  end
         | 
| 35 37 |  | 
| 36 38 | 
             
                  private
         | 
| @@ -41,7 +43,7 @@ module SplitIoClient | |
| 41 43 | 
             
                        @config.logger.debug("schedule_next_token_refresh refresh in #{time} seconds.") if @config.debug_enabled
         | 
| 42 44 | 
             
                        sleep(time)
         | 
| 43 45 | 
             
                        @config.logger.debug('schedule_next_token_refresh starting ...') if @config.debug_enabled
         | 
| 44 | 
            -
                         | 
| 46 | 
            +
                        @sse_handler.stop
         | 
| 45 47 | 
             
                        start_sse
         | 
| 46 48 | 
             
                      rescue StandardError => e
         | 
| 47 49 | 
             
                        @config.logger.debug("schedule_next_token_refresh error: #{e.inspect}") if @config.debug_enabled
         | 
| @@ -3,19 +3,13 @@ | |
| 3 3 | 
             
            module SplitIoClient
         | 
| 4 4 | 
             
              module Engine
         | 
| 5 5 | 
             
                class SyncManager
         | 
| 6 | 
            -
                  include SplitIoClient::Cache::Fetchers
         | 
| 7 | 
            -
             | 
| 8 6 | 
             
                  def initialize(
         | 
| 9 7 | 
             
                    repositories,
         | 
| 10 8 | 
             
                    api_key,
         | 
| 11 9 | 
             
                    config,
         | 
| 12 | 
            -
                     | 
| 10 | 
            +
                    synchronizer
         | 
| 13 11 | 
             
                  )
         | 
| 14 | 
            -
                     | 
| 15 | 
            -
                    segment_fetcher = SegmentFetcher.new(repositories[:segments], api_key, params[:metrics], config, params[:sdk_blocker])
         | 
| 16 | 
            -
                    sync_params = { split_fetcher: split_fetcher, segment_fetcher: segment_fetcher, imp_counter: params[:impression_counter] }
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                    @synchronizer = Synchronizer.new(repositories, api_key, config, params[:sdk_blocker], sync_params)
         | 
| 12 | 
            +
                    @synchronizer = synchronizer
         | 
| 19 13 | 
             
                    notification_manager_keeper = SplitIoClient::SSE::NotificationManagerKeeper.new(config) do |manager|
         | 
| 20 14 | 
             
                      manager.on_occupancy { |publisher_available| process_occupancy(publisher_available) }
         | 
| 21 15 | 
             
                      manager.on_push_shutdown { process_push_shutdown }
         | 
| @@ -28,10 +22,11 @@ module SplitIoClient | |
| 28 22 | 
             
                      notification_manager_keeper
         | 
| 29 23 | 
             
                    ) do |handler|
         | 
| 30 24 | 
             
                      handler.on_connected { process_connected }
         | 
| 31 | 
            -
                      handler.on_disconnect { process_disconnect }
         | 
| 25 | 
            +
                      handler.on_disconnect { |reconnect| process_disconnect(reconnect) }
         | 
| 32 26 | 
             
                    end
         | 
| 33 27 |  | 
| 34 28 | 
             
                    @push_manager = PushManager.new(config, @sse_handler, api_key)
         | 
| 29 | 
            +
                    @sse_connected = Concurrent::AtomicBoolean.new(false)
         | 
| 35 30 | 
             
                    @config = config
         | 
| 36 31 | 
             
                  end
         | 
| 37 32 |  | 
| @@ -90,6 +85,12 @@ module SplitIoClient | |
| 90 85 | 
             
                  end
         | 
| 91 86 |  | 
| 92 87 | 
             
                  def process_connected
         | 
| 88 | 
            +
                    if @sse_connected.value
         | 
| 89 | 
            +
                      @config.logger.debug('Streaming already connected.')
         | 
| 90 | 
            +
                      return
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    @sse_connected.make_true
         | 
| 93 94 | 
             
                    @synchronizer.stop_periodic_fetch
         | 
| 94 95 | 
             
                    @synchronizer.sync_all
         | 
| 95 96 | 
             
                    @sse_handler.start_workers
         | 
| @@ -97,23 +98,42 @@ module SplitIoClient | |
| 97 98 | 
             
                    @config.logger.error("process_connected error: #{e.inspect}")
         | 
| 98 99 | 
             
                  end
         | 
| 99 100 |  | 
| 100 | 
            -
                  def process_disconnect
         | 
| 101 | 
            +
                  def process_disconnect(reconnect)
         | 
| 102 | 
            +
                    unless @sse_connected.value
         | 
| 103 | 
            +
                      @config.logger.debug('Streaming already disconnected.')
         | 
| 104 | 
            +
                      return
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                    @sse_connected.make_false
         | 
| 101 108 | 
             
                    @sse_handler.stop_workers
         | 
| 102 109 | 
             
                    @synchronizer.start_periodic_fetch
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    if reconnect
         | 
| 112 | 
            +
                      @synchronizer.sync_all
         | 
| 113 | 
            +
                      @push_manager.start_sse
         | 
| 114 | 
            +
                    end
         | 
| 103 115 | 
             
                  rescue StandardError => e
         | 
| 104 116 | 
             
                    @config.logger.error("process_disconnect error: #{e.inspect}")
         | 
| 105 117 | 
             
                  end
         | 
| 106 118 |  | 
| 107 119 | 
             
                  def process_occupancy(push_enable)
         | 
| 108 | 
            -
                     | 
| 109 | 
            -
             | 
| 120 | 
            +
                    if push_enable
         | 
| 121 | 
            +
                      @synchronizer.stop_periodic_fetch
         | 
| 122 | 
            +
                      @synchronizer.sync_all
         | 
| 123 | 
            +
                      @sse_handler.start_workers
         | 
| 124 | 
            +
                      return
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    @sse_handler.stop_workers
         | 
| 128 | 
            +
                    @synchronizer.start_periodic_fetch
         | 
| 110 129 | 
             
                  rescue StandardError => e
         | 
| 111 130 | 
             
                    @config.logger.error("process_occupancy error: #{e.inspect}")
         | 
| 112 131 | 
             
                  end
         | 
| 113 132 |  | 
| 114 133 | 
             
                  def process_push_shutdown
         | 
| 115 134 | 
             
                    @push_manager.stop_sse
         | 
| 116 | 
            -
                     | 
| 135 | 
            +
                    @sse_handler.stop_workers
         | 
| 136 | 
            +
                    @synchronizer.start_periodic_fetch
         | 
| 117 137 | 
             
                  rescue StandardError => e
         | 
| 118 138 | 
             
                    @config.logger.error("process_push_shutdown error: #{e.inspect}")
         | 
| 119 139 | 
             
                  end
         | 
| @@ -28,9 +28,11 @@ module SplitIoClient | |
| 28 28 | 
             
                  end
         | 
| 29 29 |  | 
| 30 30 | 
             
                  def sync_all
         | 
| 31 | 
            -
                    @config. | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 31 | 
            +
                    @config.threads[:sync_all_thread] = Thread.new do
         | 
| 32 | 
            +
                      @config.logger.debug('Synchronizing Splits and Segments ...') if @config.debug_enabled
         | 
| 33 | 
            +
                      fetch_splits
         | 
| 34 | 
            +
                      fetch_segments
         | 
| 35 | 
            +
                    end
         | 
| 34 36 | 
             
                  end
         | 
| 35 37 |  | 
| 36 38 | 
             
                  def start_periodic_data_recording
         | 
| @@ -7,6 +7,7 @@ module SplitIoClient | |
| 7 7 | 
             
                include SplitIoClient::Cache::Repositories
         | 
| 8 8 | 
             
                include SplitIoClient::Cache::Stores
         | 
| 9 9 | 
             
                include SplitIoClient::Cache::Senders
         | 
| 10 | 
            +
                include SplitIoClient::Cache::Fetchers
         | 
| 10 11 |  | 
| 11 12 | 
             
                attr_reader :adapter, :client, :manager, :config
         | 
| 12 13 |  | 
| @@ -53,8 +54,12 @@ module SplitIoClient | |
| 53 54 | 
             
                  if @config.localhost_mode
         | 
| 54 55 | 
             
                    start_localhost_components
         | 
| 55 56 | 
             
                  else
         | 
| 56 | 
            -
                     | 
| 57 | 
            -
                     | 
| 57 | 
            +
                    split_fetcher = SplitFetcher.new(@splits_repository, @api_key, @metrics, config, @sdk_blocker)
         | 
| 58 | 
            +
                    segment_fetcher = SegmentFetcher.new(@segments_repository, @api_key, @metrics, config, @sdk_blocker)
         | 
| 59 | 
            +
                    params = { split_fetcher: split_fetcher, segment_fetcher: segment_fetcher, imp_counter: @impression_counter }
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    synchronizer = SplitIoClient::Engine::Synchronizer.new(repositories, @api_key, @config, @sdk_blocker, params)
         | 
| 62 | 
            +
                    SplitIoClient::Engine::SyncManager.new(repositories, @api_key, @config, synchronizer).start
         | 
| 58 63 | 
             
                  end
         | 
| 59 64 | 
             
                end
         | 
| 60 65 |  | 
| @@ -125,14 +130,13 @@ module SplitIoClient | |
| 125 130 | 
             
                end
         | 
| 126 131 |  | 
| 127 132 | 
             
                def repositories
         | 
| 128 | 
            -
                   | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 135 | 
            -
                  repos
         | 
| 133 | 
            +
                  { 
         | 
| 134 | 
            +
                    splits: @splits_repository,
         | 
| 135 | 
            +
                    segments: @segments_repository,
         | 
| 136 | 
            +
                    impressions: @impressions_repository,
         | 
| 137 | 
            +
                    events: @events_repository,
         | 
| 138 | 
            +
                    metrics: @metrics_repository
         | 
| 139 | 
            +
                  }
         | 
| 136 140 | 
             
                end
         | 
| 137 141 |  | 
| 138 142 | 
             
                def start_localhost_components
         | 
| @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: false
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'concurrent/atomics'
         | 
| 4 3 | 
             
            require 'socketry'
         | 
| 5 4 | 
             
            require 'uri'
         | 
| 6 5 |  | 
| @@ -9,15 +8,16 @@ module SplitIoClient | |
| 9 8 | 
             
                module EventSource
         | 
| 10 9 | 
             
                  class Client
         | 
| 11 10 | 
             
                    DEFAULT_READ_TIMEOUT = 70
         | 
| 11 | 
            +
                    CONNECT_TIMEOUT = 30_000
         | 
| 12 12 | 
             
                    KEEP_ALIVE_RESPONSE = "c\r\n:keepalive\n\n\r\n".freeze
         | 
| 13 | 
            +
                    ERROR_EVENT_TYPE = 'error'.freeze
         | 
| 13 14 |  | 
| 14 15 | 
             
                    def initialize(config, read_timeout: DEFAULT_READ_TIMEOUT)
         | 
| 15 16 | 
             
                      @config = config
         | 
| 16 17 | 
             
                      @read_timeout = read_timeout
         | 
| 17 18 | 
             
                      @connected = Concurrent::AtomicBoolean.new(false)
         | 
| 18 19 | 
             
                      @socket = nil
         | 
| 19 | 
            -
                      @ | 
| 20 | 
            -
             | 
| 20 | 
            +
                      @event_parser = SSE::EventSource::EventParser.new(config)
         | 
| 21 21 | 
             
                      @on = { event: ->(_) {}, connected: ->(_) {}, disconnect: ->(_) {} }
         | 
| 22 22 |  | 
| 23 23 | 
             
                      yield self if block_given?
         | 
| @@ -35,8 +35,8 @@ module SplitIoClient | |
| 35 35 | 
             
                      @on[:disconnect] = action
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 | 
            -
                    def close
         | 
| 39 | 
            -
                      dispatch_disconnect
         | 
| 38 | 
            +
                    def close(reconnect = false)
         | 
| 39 | 
            +
                      dispatch_disconnect(reconnect)
         | 
| 40 40 | 
             
                      @connected.make_false
         | 
| 41 41 | 
             
                      SplitIoClient::Helpers::ThreadHelper.stop(:connect_stream, @config)
         | 
| 42 42 | 
             
                      @socket&.close
         | 
| @@ -46,10 +46,16 @@ module SplitIoClient | |
| 46 46 |  | 
| 47 47 | 
             
                    def start(url)
         | 
| 48 48 | 
             
                      @uri = URI(url)
         | 
| 49 | 
            +
                      latch = Concurrent::CountDownLatch.new(1)
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      connect_thread(latch)
         | 
| 49 52 |  | 
| 50 | 
            -
                       | 
| 53 | 
            +
                      return false unless latch.wait(CONNECT_TIMEOUT)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      connected?
         | 
| 51 56 | 
             
                    rescue StandardError => e
         | 
| 52 57 | 
             
                      @config.logger.error("SSEClient start Error: #{e.inspect}")
         | 
| 58 | 
            +
                      connected?
         | 
| 53 59 | 
             
                    end
         | 
| 54 60 |  | 
| 55 61 | 
             
                    def connected?
         | 
| @@ -58,18 +64,15 @@ module SplitIoClient | |
| 58 64 |  | 
| 59 65 | 
             
                    private
         | 
| 60 66 |  | 
| 61 | 
            -
                    def connect_thread
         | 
| 67 | 
            +
                    def connect_thread(latch)
         | 
| 62 68 | 
             
                      @config.threads[:connect_stream] = Thread.new do
         | 
| 63 69 | 
             
                        @config.logger.info('Starting connect_stream thread ...') if @config.debug_enabled
         | 
| 64 | 
            -
                        connect_stream
         | 
| 70 | 
            +
                        connect_stream(latch)
         | 
| 65 71 | 
             
                      end
         | 
| 66 72 | 
             
                    end
         | 
| 67 73 |  | 
| 68 | 
            -
                    def connect_stream
         | 
| 69 | 
            -
                       | 
| 70 | 
            -
                      sleep(interval) if interval.positive?
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                      socket_write
         | 
| 74 | 
            +
                    def connect_stream(latch)
         | 
| 75 | 
            +
                      socket_write(latch)
         | 
| 73 76 |  | 
| 74 77 | 
             
                      while @connected.value
         | 
| 75 78 | 
             
                        begin
         | 
| @@ -77,24 +80,24 @@ module SplitIoClient | |
| 77 80 |  | 
| 78 81 | 
             
                          raise 'eof exception' if partial_data == :eof
         | 
| 79 82 | 
             
                        rescue StandardError => e
         | 
| 80 | 
            -
                          @config.logger.error(e.inspect) if @config.debug_enabled
         | 
| 81 | 
            -
                           | 
| 82 | 
            -
                           | 
| 83 | 
            -
                          @socket = nil
         | 
| 84 | 
            -
                          connect_stream
         | 
| 83 | 
            +
                          @config.logger.error('Error reading partial data: ' + e.inspect) if @config.debug_enabled
         | 
| 84 | 
            +
                          close(true) # close conexion & reconnect
         | 
| 85 | 
            +
                          return
         | 
| 85 86 | 
             
                        end
         | 
| 86 87 |  | 
| 87 88 | 
             
                        process_data(partial_data)
         | 
| 88 89 | 
             
                      end
         | 
| 89 90 | 
             
                    end
         | 
| 90 91 |  | 
| 91 | 
            -
                    def socket_write
         | 
| 92 | 
            +
                    def socket_write(latch)
         | 
| 92 93 | 
             
                      @socket = socket_connect
         | 
| 93 94 | 
             
                      @socket.write(build_request(@uri))
         | 
| 94 95 | 
             
                      dispatch_connected
         | 
| 95 96 | 
             
                    rescue StandardError => e
         | 
| 96 97 | 
             
                      @config.logger.error("Error during connecting to #{@uri.host}. Error: #{e.inspect}")
         | 
| 97 98 | 
             
                      close
         | 
| 99 | 
            +
                    ensure
         | 
| 100 | 
            +
                      latch.count_down
         | 
| 98 101 | 
             
                    end
         | 
| 99 102 |  | 
| 100 103 | 
             
                    def socket_connect
         | 
| @@ -104,11 +107,11 @@ module SplitIoClient | |
| 104 107 | 
             
                    end
         | 
| 105 108 |  | 
| 106 109 | 
             
                    def process_data(partial_data)
         | 
| 107 | 
            -
                       | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
                       | 
| 110 | 
            +
                      return if partial_data.nil? || partial_data == KEEP_ALIVE_RESPONSE
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                      @config.logger.debug("Event partial data: #{partial_data}") if @config.debug_enabled
         | 
| 113 | 
            +
                      events = @event_parser.parse(partial_data)
         | 
| 114 | 
            +
                      events.each { |event| process_event(event) }
         | 
| 112 115 | 
             
                    rescue StandardError => e
         | 
| 113 116 | 
             
                      @config.logger.error("process_data error: #{e.inspect}")
         | 
| 114 117 | 
             
                    end
         | 
| @@ -122,64 +125,38 @@ module SplitIoClient | |
| 122 125 | 
             
                      req
         | 
| 123 126 | 
             
                    end
         | 
| 124 127 |  | 
| 125 | 
            -
                    def  | 
| 126 | 
            -
                       | 
| 127 | 
            -
                       | 
| 128 | 
            -
             | 
| 129 | 
            -
                       | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
                    def parse_event(buffer)
         | 
| 133 | 
            -
                      type = nil
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                      buffer.each do |d|
         | 
| 136 | 
            -
                        splited_data = d.split(':')
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                        case splited_data[0]
         | 
| 139 | 
            -
                        when 'event'
         | 
| 140 | 
            -
                          type = splited_data[1].strip
         | 
| 141 | 
            -
                        when 'data'
         | 
| 142 | 
            -
                          data = parse_event_data(d, type)
         | 
| 143 | 
            -
                          unless type.nil? || data[:data].nil?
         | 
| 144 | 
            -
                            event = StreamData.new(type, data[:client_id], data[:data], data[:channel])
         | 
| 145 | 
            -
                            dispatch_event(event)
         | 
| 146 | 
            -
                          end
         | 
| 147 | 
            -
                        end
         | 
| 128 | 
            +
                    def process_event(event)
         | 
| 129 | 
            +
                      case event.event_type
         | 
| 130 | 
            +
                      when ERROR_EVENT_TYPE
         | 
| 131 | 
            +
                        dispatch_error(event)
         | 
| 132 | 
            +
                      else
         | 
| 133 | 
            +
                        dispatch_event(event)
         | 
| 148 134 | 
             
                      end
         | 
| 149 | 
            -
                    rescue StandardError => e
         | 
| 150 | 
            -
                      @config.logger.error("Error during parsing a event: #{e.inspect}")
         | 
| 151 135 | 
             
                    end
         | 
| 152 136 |  | 
| 153 | 
            -
                    def  | 
| 154 | 
            -
                       | 
| 155 | 
            -
                       | 
| 156 | 
            -
             | 
| 157 | 
            -
                       | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 160 | 
            -
                      { client_id: client_id, channel: channel, data: parsed_data }
         | 
| 137 | 
            +
                    def dispatch_error(event)
         | 
| 138 | 
            +
                      @config.logger.error("Event error: #{event.event_type}, #{event.data}")
         | 
| 139 | 
            +
                      if event.data['code'] >= 40_140 && event.data['code'] <= 40_149
         | 
| 140 | 
            +
                        close(true) # close conexion & reconnect
         | 
| 141 | 
            +
                      elsif event.data['code'] >= 40_000 && event.data['code'] <= 49_999
         | 
| 142 | 
            +
                        close # close conexion
         | 
| 143 | 
            +
                      end
         | 
| 161 144 | 
             
                    end
         | 
| 162 145 |  | 
| 163 146 | 
             
                    def dispatch_event(event)
         | 
| 164 | 
            -
                      raise SSEClientException.new(event), 'Error event' if event.event_type == 'error'
         | 
| 165 | 
            -
             | 
| 166 147 | 
             
                      @config.logger.debug("Dispatching event: #{event.event_type}, #{event.channel}") if @config.debug_enabled
         | 
| 167 148 | 
             
                      @on[:event].call(event)
         | 
| 168 | 
            -
                    rescue SSEClientException => e
         | 
| 169 | 
            -
                      @config.logger.error("Event error: #{e.event.event_type}, #{e.event.data}")
         | 
| 170 | 
            -
                      close
         | 
| 171 149 | 
             
                    end
         | 
| 172 150 |  | 
| 173 151 | 
             
                    def dispatch_connected
         | 
| 174 152 | 
             
                      @connected.make_true
         | 
| 175 | 
            -
                      @back_off.reset
         | 
| 176 153 | 
             
                      @config.logger.debug('Dispatching connected') if @config.debug_enabled
         | 
| 177 154 | 
             
                      @on[:connected].call
         | 
| 178 155 | 
             
                    end
         | 
| 179 156 |  | 
| 180 | 
            -
                    def dispatch_disconnect
         | 
| 157 | 
            +
                    def dispatch_disconnect(reconnect)
         | 
| 181 158 | 
             
                      @config.logger.debug('Dispatching disconnect') if @config.debug_enabled
         | 
| 182 | 
            -
                      @on[:disconnect].call
         | 
| 159 | 
            +
                      @on[:disconnect].call(reconnect)
         | 
| 183 160 | 
             
                    end
         | 
| 184 161 | 
             
                  end
         | 
| 185 162 | 
             
                end
         | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            # frozen_string_literal: false
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module SplitIoClient
         | 
| 4 | 
            +
              module SSE
         | 
| 5 | 
            +
                module EventSource
         | 
| 6 | 
            +
                  class EventParser
         | 
| 7 | 
            +
                    def initialize(config)
         | 
| 8 | 
            +
                      @config = config
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def parse(raw_event)
         | 
| 12 | 
            +
                      type = nil
         | 
| 13 | 
            +
                      events = []
         | 
| 14 | 
            +
                      buffer = read_partial_data(raw_event)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      buffer.each do |d|
         | 
| 17 | 
            +
                        splited_data = d.split(':')
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                        case splited_data[0]
         | 
| 20 | 
            +
                        when 'event'
         | 
| 21 | 
            +
                          type = splited_data[1].strip
         | 
| 22 | 
            +
                        when 'data'
         | 
| 23 | 
            +
                          data = parse_event_data(d, type)
         | 
| 24 | 
            +
                          events << StreamData.new(type, data[:client_id], data[:data], data[:channel]) unless type.nil? || data[:data].nil?
         | 
| 25 | 
            +
                        end
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      events
         | 
| 29 | 
            +
                    rescue StandardError => e
         | 
| 30 | 
            +
                      @config.logger.error("Error during parsing a event: #{e.inspect}")
         | 
| 31 | 
            +
                      []
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    private
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    def parse_event_data(data, type)
         | 
| 37 | 
            +
                      event_data = JSON.parse(data.sub('data: ', ''))
         | 
| 38 | 
            +
                      client_id = event_data['clientId']&.strip
         | 
| 39 | 
            +
                      channel = event_data['channel']&.strip
         | 
| 40 | 
            +
                      parsed_data = JSON.parse(event_data['data']) unless type == 'error'
         | 
| 41 | 
            +
                      parsed_data = event_data if type == 'error'
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                      { client_id: client_id, channel: channel, data: parsed_data }
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    def read_partial_data(data)
         | 
| 47 | 
            +
                      buffer = ''
         | 
| 48 | 
            +
                      buffer << data
         | 
| 49 | 
            +
                      buffer.chomp!
         | 
| 50 | 
            +
                      buffer.split("\n")
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
| @@ -14,7 +14,7 @@ module SplitIoClient | |
| 14 14 | 
             
                    @sse_client = SSE::EventSource::Client.new(@config) do |client|
         | 
| 15 15 | 
             
                      client.on_event { |event| handle_incoming_message(event) }
         | 
| 16 16 | 
             
                      client.on_connected { process_connected }
         | 
| 17 | 
            -
                      client.on_disconnect { process_disconnect }
         | 
| 17 | 
            +
                      client.on_disconnect { |reconnect| process_disconnect(reconnect) }
         | 
| 18 18 | 
             
                    end
         | 
| 19 19 |  | 
| 20 20 | 
             
                    @on = { connected: ->(_) {}, disconnect: ->(_) {} }
         | 
| @@ -56,8 +56,8 @@ module SplitIoClient | |
| 56 56 | 
             
                    @on[:disconnect] = action
         | 
| 57 57 | 
             
                  end
         | 
| 58 58 |  | 
| 59 | 
            -
                  def process_disconnect
         | 
| 60 | 
            -
                    @on[:disconnect].call
         | 
| 59 | 
            +
                  def process_disconnect(reconnect)
         | 
| 60 | 
            +
                    @on[:disconnect].call(reconnect)
         | 
| 61 61 | 
             
                  end
         | 
| 62 62 |  | 
| 63 63 | 
             
                  private
         | 
| @@ -8,27 +8,39 @@ module SplitIoClient | |
| 8 8 | 
             
                      @synchronizer = synchronizer
         | 
| 9 9 | 
             
                      @config = config
         | 
| 10 10 | 
             
                      @segments_repository = segments_repository
         | 
| 11 | 
            -
                      @queue = nil
         | 
| 12 | 
            -
                    end
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                    def start
         | 
| 15 | 
            -
                      return if SplitIoClient::Helpers::ThreadHelper.alive?(:segment_update_worker, @config)
         | 
| 16 | 
            -
             | 
| 17 11 | 
             
                      @queue = Queue.new
         | 
| 18 | 
            -
                       | 
| 12 | 
            +
                      @running = Concurrent::AtomicBoolean.new(false)
         | 
| 19 13 | 
             
                    end
         | 
| 20 14 |  | 
| 21 15 | 
             
                    def add_to_queue(change_number, segment_name)
         | 
| 22 | 
            -
                       | 
| 16 | 
            +
                      unless @running.value
         | 
| 17 | 
            +
                        @config.logger.debug('segments worker not running.')
         | 
| 18 | 
            +
                        return
         | 
| 19 | 
            +
                      end
         | 
| 23 20 |  | 
| 24 21 | 
             
                      item = { change_number: change_number, segment_name: segment_name }
         | 
| 25 22 | 
             
                      @config.logger.debug("SegmentsWorker add to queue #{item}")
         | 
| 26 23 | 
             
                      @queue.push(item)
         | 
| 27 24 | 
             
                    end
         | 
| 28 25 |  | 
| 26 | 
            +
                    def start
         | 
| 27 | 
            +
                      if @running.value
         | 
| 28 | 
            +
                        @config.logger.debug('segments worker already running.')
         | 
| 29 | 
            +
                        return
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      @running.make_true
         | 
| 33 | 
            +
                      perform_thread
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 29 36 | 
             
                    def stop
         | 
| 37 | 
            +
                      unless @running.value
         | 
| 38 | 
            +
                        @config.logger.debug('segments worker not running.')
         | 
| 39 | 
            +
                        return
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                      @running.make_false
         | 
| 30 43 | 
             
                      SplitIoClient::Helpers::ThreadHelper.stop(:segment_update_worker, @config)
         | 
| 31 | 
            -
                      @queue = nil
         | 
| 32 44 | 
             
                    end
         | 
| 33 45 |  | 
| 34 46 | 
             
                    private
         | 
| @@ -8,35 +8,53 @@ module SplitIoClient | |
| 8 8 | 
             
                      @synchronizer = synchronizer
         | 
| 9 9 | 
             
                      @config = config
         | 
| 10 10 | 
             
                      @splits_repository = splits_repository
         | 
| 11 | 
            +
                      @queue = Queue.new
         | 
| 12 | 
            +
                      @running = Concurrent::AtomicBoolean.new(false)
         | 
| 11 13 | 
             
                    end
         | 
| 12 14 |  | 
| 13 15 | 
             
                    def start
         | 
| 14 | 
            -
                       | 
| 16 | 
            +
                      if @running.value
         | 
| 17 | 
            +
                        @config.logger.debug('splits worker already running.')
         | 
| 18 | 
            +
                        return
         | 
| 19 | 
            +
                      end
         | 
| 15 20 |  | 
| 16 | 
            -
                      @ | 
| 21 | 
            +
                      @running.make_true
         | 
| 17 22 | 
             
                      perform_thread
         | 
| 18 23 | 
             
                    end
         | 
| 19 24 |  | 
| 25 | 
            +
                    def stop
         | 
| 26 | 
            +
                      unless @running.value
         | 
| 27 | 
            +
                        @config.logger.debug('splits worker not running.')
         | 
| 28 | 
            +
                        return
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      @running.make_false
         | 
| 32 | 
            +
                      SplitIoClient::Helpers::ThreadHelper.stop(:split_update_worker, @config)
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 20 35 | 
             
                    def add_to_queue(change_number)
         | 
| 21 | 
            -
                       | 
| 36 | 
            +
                      unless @running.value
         | 
| 37 | 
            +
                        @config.logger.debug('splits worker not running.')
         | 
| 38 | 
            +
                        return
         | 
| 39 | 
            +
                      end
         | 
| 22 40 |  | 
| 23 41 | 
             
                      @config.logger.debug("SplitsWorker add to queue #{change_number}")
         | 
| 24 42 | 
             
                      @queue.push(change_number)
         | 
| 25 43 | 
             
                    end
         | 
| 26 44 |  | 
| 27 45 | 
             
                    def kill_split(change_number, split_name, default_treatment)
         | 
| 28 | 
            -
                       | 
| 46 | 
            +
                      unless @running.value
         | 
| 47 | 
            +
                        @config.logger.debug('splits worker not running.')
         | 
| 48 | 
            +
                        return
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      return if @splits_repository.get_change_number.to_i > change_number
         | 
| 29 52 |  | 
| 30 53 | 
             
                      @config.logger.debug("SplitsWorker kill #{split_name}, #{change_number}")
         | 
| 31 54 | 
             
                      @splits_repository.kill(change_number, split_name, default_treatment)
         | 
| 32 55 | 
             
                      add_to_queue(change_number)
         | 
| 33 56 | 
             
                    end
         | 
| 34 57 |  | 
| 35 | 
            -
                    def stop
         | 
| 36 | 
            -
                      SplitIoClient::Helpers::ThreadHelper.stop(:split_update_worker, @config)
         | 
| 37 | 
            -
                      @queue = nil
         | 
| 38 | 
            -
                    end
         | 
| 39 | 
            -
             | 
| 40 58 | 
             
                    private
         | 
| 41 59 |  | 
| 42 60 | 
             
                    def perform
         | 
    
        data/splitclient-rb.gemspec
    CHANGED
    
    | @@ -55,7 +55,7 @@ Gem::Specification.new do |spec| | |
| 55 55 | 
             
              spec.add_runtime_dependency 'jwt', '>= 2.2.1'
         | 
| 56 56 | 
             
              spec.add_runtime_dependency 'lru_redux'
         | 
| 57 57 | 
             
              spec.add_runtime_dependency 'net-http-persistent', '>= 2.9'
         | 
| 58 | 
            -
              spec.add_runtime_dependency 'redis', '>=  | 
| 58 | 
            +
              spec.add_runtime_dependency 'redis', '>= 4.2.2'
         | 
| 59 59 | 
             
              spec.add_runtime_dependency 'socketry', '~> 0.5.1'
         | 
| 60 60 | 
             
              spec.add_runtime_dependency 'thread_safe', '>= 0.3'
         | 
| 61 61 | 
             
            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. | 
| 4 | 
            +
              version: 7.2.2.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- | 
| 11 | 
            +
            date: 2020-12-16 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -267,7 +267,7 @@ dependencies: | |
| 267 267 | 
             
                requirements:
         | 
| 268 268 | 
             
                - - ">="
         | 
| 269 269 | 
             
                  - !ruby/object:Gem::Version
         | 
| 270 | 
            -
                    version:  | 
| 270 | 
            +
                    version: 4.2.2
         | 
| 271 271 | 
             
              name: redis
         | 
| 272 272 | 
             
              prerelease: false
         | 
| 273 273 | 
             
              type: :runtime
         | 
| @@ -275,7 +275,7 @@ dependencies: | |
| 275 275 | 
             
                requirements:
         | 
| 276 276 | 
             
                - - ">="
         | 
| 277 277 | 
             
                  - !ruby/object:Gem::Version
         | 
| 278 | 
            -
                    version:  | 
| 278 | 
            +
                    version: 4.2.2
         | 
| 279 279 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 280 280 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 281 281 | 
             
                requirements:
         | 
| @@ -420,6 +420,7 @@ files: | |
| 420 420 | 
             
            - lib/splitclient-rb/split_logger.rb
         | 
| 421 421 | 
             
            - lib/splitclient-rb/sse/event_source/back_off.rb
         | 
| 422 422 | 
             
            - lib/splitclient-rb/sse/event_source/client.rb
         | 
| 423 | 
            +
            - lib/splitclient-rb/sse/event_source/event_parser.rb
         | 
| 423 424 | 
             
            - lib/splitclient-rb/sse/event_source/event_types.rb
         | 
| 424 425 | 
             
            - lib/splitclient-rb/sse/event_source/stream_data.rb
         | 
| 425 426 | 
             
            - lib/splitclient-rb/sse/notification_manager_keeper.rb
         |