splitclient-rb 7.3.0-java → 7.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d328b7af04f989ab96799170e99c58393523c82
4
- data.tar.gz: d11d77937b94288a2e0d2e950374e3170a0e823f
3
+ metadata.gz: e4c38ff221566b6a030d3701c703ab5f73ca94fb
4
+ data.tar.gz: '081b5b1132ed42843658e3a33bcce62d1465dac3'
5
5
  SHA512:
6
- metadata.gz: b19de8c12409c8cf266ca749dc928bb6f9530f8a2d2ddc8658754afd92866a3eed322ad8474a4536114c8ebd0d9c7ff2e0c75dcd6525dbe12f2680ea48148c23
7
- data.tar.gz: 36828b1670d308a0faaab7fd8fb8d767dd58b2dcecaab2b0defb485551007f35a257bf0f0b1a8e331f8813c25c92ff39c1bbf720a2fb2d293deb0fbb7727488b
6
+ metadata.gz: c5b7a1e7d7332bfd1180974a57890e3e2f4919a14dba9ed7ee137906a803ae38a733cea56f837a35ed20801d085184cd9bf65656cdb16fc774f79a117c980ea3
7
+ data.tar.gz: 7e849dda094c87cd2495749be25aa877bf6b3e91c420acfee38bb38cf9604c7fffe58bd7a80669837f918d50762fb2d2ee7800889515a6abb2f1543de64956d7
data/.rubocop.yml CHANGED
@@ -31,6 +31,7 @@ Metrics/LineLength:
31
31
  - spec/engine/sync_manager_spec.rb
32
32
  - spec/engine/auth_api_client_spec.rb
33
33
  - spec/telemetry/synchronizer_spec.rb
34
+ - spec/splitclient/split_config_spec.rb
34
35
 
35
36
  Style/BracesAroundHashParameters:
36
37
  Exclude:
@@ -62,3 +63,4 @@ AllCops:
62
63
  - lib/splitclient-rb/engine/models/**/*
63
64
  - lib/splitclient-rb/engine/parser/**/*
64
65
  - spec/telemetry/synchronizer_spec.rb
66
+ - lib/splitclient-rb/engine/synchronizer.rb
@@ -85,13 +85,13 @@ require 'splitclient-rb/engine/models/split'
85
85
  require 'splitclient-rb/engine/models/label'
86
86
  require 'splitclient-rb/engine/models/treatment'
87
87
  require 'splitclient-rb/engine/auth_api_client'
88
+ require 'splitclient-rb/engine/back_off'
88
89
  require 'splitclient-rb/engine/push_manager'
89
90
  require 'splitclient-rb/engine/sync_manager'
90
91
  require 'splitclient-rb/engine/synchronizer'
91
92
  require 'splitclient-rb/utilitites'
92
93
 
93
- # SSE
94
- require 'splitclient-rb/sse/event_source/back_off'
94
+ # SSE
95
95
  require 'splitclient-rb/sse/event_source/client'
96
96
  require 'splitclient-rb/sse/event_source/event_parser'
97
97
  require 'splitclient-rb/sse/event_source/event_types'
@@ -30,16 +30,19 @@ module SplitIoClient
30
30
  def fetch_segments_if_not_exists(names, cache_control_headers = false)
31
31
  names.each do |name|
32
32
  change_number = @segments_repository.get_change_number(name)
33
-
34
- fetch_segment(name, cache_control_headers) if change_number == -1
33
+
34
+ if change_number == -1
35
+ fetch_options = { cache_control_headers: cache_control_headers, till: nil }
36
+ fetch_segment(name, fetch_options) if change_number == -1
37
+ end
35
38
  end
36
39
  rescue StandardError => error
37
40
  @config.log_found_exception(__method__.to_s, error)
38
41
  end
39
42
 
40
- def fetch_segment(name, cache_control_headers = false)
43
+ def fetch_segment(name, fetch_options = { cache_control_headers: false, till: nil })
41
44
  @semaphore.synchronize do
42
- segments_api.fetch_segments_by_names([name], cache_control_headers)
45
+ segments_api.fetch_segments_by_names([name], fetch_options)
43
46
  end
44
47
  rescue StandardError => error
45
48
  @config.log_found_exception(__method__.to_s, error)
@@ -27,9 +27,9 @@ module SplitIoClient
27
27
  end
28
28
  end
29
29
 
30
- def fetch_splits(cache_control_headers = false)
30
+ def fetch_splits(fetch_options = { cache_control_headers: false, till: nil })
31
31
  @semaphore.synchronize do
32
- data = splits_since(@splits_repository.get_change_number, cache_control_headers)
32
+ data = splits_since(@splits_repository.get_change_number, fetch_options)
33
33
 
34
34
  data[:splits] && data[:splits].each do |split|
35
35
  add_split_unless_archived(split)
@@ -68,8 +68,8 @@ module SplitIoClient
68
68
  end
69
69
  end
70
70
 
71
- def splits_since(since, cache_control_headers = false)
72
- splits_api.since(since, cache_control_headers)
71
+ def splits_since(since, fetch_options = { cache_control_headers: false, till: nil })
72
+ splits_api.since(since, fetch_options)
73
73
  end
74
74
 
75
75
  def add_split_unless_archived(split)
@@ -11,13 +11,14 @@ module SplitIoClient
11
11
  @telemetry_runtime_producer = telemetry_runtime_producer
12
12
  end
13
13
 
14
- def fetch_segments_by_names(names, cache_control_headers = false)
14
+ def fetch_segments_by_names(names, fetch_options = { cache_control_headers: false, till: nil })
15
15
  return if names.nil? || names.empty?
16
16
 
17
17
  names.each do |name|
18
18
  since = @segments_repository.get_change_number(name)
19
+
19
20
  loop do
20
- segment = fetch_segment_changes(name, since, cache_control_headers)
21
+ segment = fetch_segment_changes(name, since, fetch_options)
21
22
  @segments_repository.add_to_segment(segment)
22
23
 
23
24
  @config.split_logger.log_if_debug("Segment #{name} fetched before: #{since}, \
@@ -32,9 +33,12 @@ module SplitIoClient
32
33
 
33
34
  private
34
35
 
35
- def fetch_segment_changes(name, since, cache_control_headers = false)
36
+ def fetch_segment_changes(name, since, fetch_options = { cache_control_headers: false, till: nil })
36
37
  start = Time.now
37
- response = get_api("#{@config.base_uri}/segmentChanges/#{name}", @api_key, { since: since }, cache_control_headers)
38
+
39
+ params = { since: since }
40
+ params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
41
+ response = get_api("#{@config.base_uri}/segmentChanges/#{name}", @api_key, params, fetch_options[:cache_control_headers])
38
42
 
39
43
  if response.success?
40
44
  segment = JSON.parse(response.body, symbolize_names: true)
@@ -10,10 +10,12 @@ module SplitIoClient
10
10
  @telemetry_runtime_producer = telemetry_runtime_producer
11
11
  end
12
12
 
13
- def since(since, cache_control_headers = false)
13
+ def since(since, fetch_options = { cache_control_headers: false, till: nil })
14
14
  start = Time.now
15
-
16
- response = get_api("#{@config.base_uri}/splitChanges", @api_key, { since: since }, cache_control_headers)
15
+
16
+ params = { since: since }
17
+ params[:till] = fetch_options[:till] unless fetch_options[:till].nil?
18
+ response = get_api("#{@config.base_uri}/splitChanges", @api_key, params, fetch_options[:cache_control_headers])
17
19
  if response.success?
18
20
  result = splits_with_segment_names(response.body)
19
21
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: false
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ BACKOFF_MAX_ALLOWED = 1.8
6
+ class BackOff
7
+ def initialize(back_off_base, attempt = 0, max_allowed = BACKOFF_MAX_ALLOWED)
8
+ @attempt = attempt
9
+ @back_off_base = back_off_base
10
+ @max_allowed = max_allowed
11
+ end
12
+
13
+ def interval
14
+ interval = 0
15
+ interval = (@back_off_base * (2**@attempt)) if @attempt.positive?
16
+ @attempt += 1
17
+
18
+ interval >= @max_allowed ? @max_allowed : interval
19
+ end
20
+
21
+ def reset
22
+ @attempt = 0
23
+ end
24
+ end
25
+ end
26
+ end
@@ -8,7 +8,7 @@ module SplitIoClient
8
8
  @sse_handler = sse_handler
9
9
  @auth_api_client = AuthApiClient.new(@config, telemetry_runtime_producer)
10
10
  @api_key = api_key
11
- @back_off = SplitIoClient::SSE::EventSource::BackOff.new(@config.auth_retry_back_off_base, 1)
11
+ @back_off = Engine::BackOff.new(@config.auth_retry_back_off_base, 1)
12
12
  @telemetry_runtime_producer = telemetry_runtime_producer
13
13
  end
14
14
 
@@ -6,7 +6,9 @@ module SplitIoClient
6
6
  include SplitIoClient::Cache::Fetchers
7
7
  include SplitIoClient::Cache::Senders
8
8
 
9
- FORCE_CACHE_CONTROL_HEADERS = true
9
+ ON_DEMAND_FETCH_BACKOFF_BASE_SECONDS = 10
10
+ ON_DEMAND_FETCH_BACKOFF_MAX_WAIT_SECONDS = 60
11
+ ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES = 10
10
12
 
11
13
  def initialize(
12
14
  repositories,
@@ -54,17 +56,116 @@ module SplitIoClient
54
56
  @segment_fetcher.stop_segments_thread
55
57
  end
56
58
 
57
- def fetch_splits
58
- segment_names = @split_fetcher.fetch_splits(FORCE_CACHE_CONTROL_HEADERS)
59
- @segment_fetcher.fetch_segments_if_not_exists(segment_names, FORCE_CACHE_CONTROL_HEADERS) unless segment_names.empty?
59
+ def fetch_splits(target_change_number)
60
+ return if target_change_number <= @splits_repository.get_change_number.to_i
61
+
62
+ fetch_options = { cache_control_headers: true, till: nil }
63
+
64
+ result = attempt_splits_sync(target_change_number,
65
+ fetch_options,
66
+ @config.on_demand_fetch_max_retries,
67
+ @config.on_demand_fetch_retry_delay_seconds,
68
+ false)
69
+
70
+ attempts = @config.on_demand_fetch_max_retries - result[:remaining_attempts]
71
+ if result[:success]
72
+ @segment_fetcher.fetch_segments_if_not_exists(result[:segment_names], true) unless result[:segment_names].empty?
73
+ @config.logger.debug("Refresh completed in #{attempts} attempts.") if @config.debug_enabled
74
+
75
+ return
76
+ end
77
+
78
+ fetch_options[:till] = target_change_number
79
+ result = attempt_splits_sync(target_change_number,
80
+ fetch_options,
81
+ ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES,
82
+ nil,
83
+ true)
84
+
85
+ attempts = @config.on_demand_fetch_max_retries - result[:remaining_attempts]
86
+
87
+ if result[:success]
88
+ @segment_fetcher.fetch_segments_if_not_exists(result[:segment_names], true) unless result[:segment_names].empty?
89
+ @config.logger.debug("Refresh completed bypassing the CDN in #{attempts} attempts.") if @config.debug_enabled
90
+ else
91
+ @config.logger.debug("No changes fetched after #{attempts} attempts with CDN bypassed.") if @config.debug_enabled
92
+ end
93
+ rescue StandardError => error
94
+ @config.log_found_exception(__method__.to_s, error)
60
95
  end
61
96
 
62
- def fetch_segment(name)
63
- @segment_fetcher.fetch_segment(name, FORCE_CACHE_CONTROL_HEADERS)
97
+ def fetch_segment(name, target_change_number)
98
+ return if target_change_number <= @segments_repository.get_change_number(name).to_i
99
+
100
+ fetch_options = { cache_control_headers: true, till: nil }
101
+ result = attempt_segment_sync(name,
102
+ target_change_number,
103
+ fetch_options,
104
+ @config.on_demand_fetch_max_retries,
105
+ @config.on_demand_fetch_retry_delay_seconds,
106
+ false)
107
+
108
+ attempts = @config.on_demand_fetch_max_retries - result[:remaining_attempts]
109
+ if result[:success]
110
+ @config.logger.debug("Segment #{name} refresh completed in #{attempts} attempts.") if @config.debug_enabled
111
+
112
+ return
113
+ end
114
+
115
+ fetch_options = { cache_control_headers: true, till: target_change_number }
116
+ result = attempt_segment_sync(name,
117
+ target_change_number,
118
+ fetch_options,
119
+ ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES,
120
+ nil,
121
+ true)
122
+
123
+ attempts = @config.on_demand_fetch_max_retries - result[:remaining_attempts]
124
+ if result[:success]
125
+ @config.logger.debug("Segment #{name} refresh completed bypassing the CDN in #{attempts} attempts.") if @config.debug_enabled
126
+ else
127
+ @config.logger.debug("No changes fetched for segment #{name} after #{attempts} attempts with CDN bypassed.") if @config.debug_enabled
128
+ end
129
+ rescue StandardError => error
130
+ @config.log_found_exception(__method__.to_s, error)
64
131
  end
65
132
 
66
133
  private
67
134
 
135
+ def attempt_segment_sync(name, target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff)
136
+ remaining_attempts = max_retries
137
+ backoff = Engine::BackOff.new(ON_DEMAND_FETCH_BACKOFF_BASE_SECONDS, 0, ON_DEMAND_FETCH_BACKOFF_MAX_WAIT_SECONDS) if with_backoff
138
+
139
+ loop do
140
+ remaining_attempts -= 1
141
+
142
+ @segment_fetcher.fetch_segment(name, fetch_options)
143
+
144
+ return sync_result(true, remaining_attempts) if target_cn <= @segments_repository.get_change_number(name).to_i
145
+ return sync_result(false, remaining_attempts) if remaining_attempts <= 0
146
+
147
+ delay = with_backoff ? backoff.interval : retry_delay_seconds
148
+ sleep(delay)
149
+ end
150
+ end
151
+
152
+ def attempt_splits_sync(target_cn, fetch_options, max_retries, retry_delay_seconds, with_backoff)
153
+ remaining_attempts = max_retries
154
+ backoff = Engine::BackOff.new(ON_DEMAND_FETCH_BACKOFF_BASE_SECONDS, 0, ON_DEMAND_FETCH_BACKOFF_MAX_WAIT_SECONDS) if with_backoff
155
+
156
+ loop do
157
+ remaining_attempts -= 1
158
+
159
+ segment_names = @split_fetcher.fetch_splits(fetch_options)
160
+
161
+ return sync_result(true, remaining_attempts, segment_names) if target_cn <= @splits_repository.get_change_number
162
+ return sync_result(false, remaining_attempts, segment_names) if remaining_attempts <= 0
163
+
164
+ delay = with_backoff ? backoff.interval : retry_delay_seconds
165
+ sleep(delay)
166
+ end
167
+ end
168
+
68
169
  def fetch_segments
69
170
  @segment_fetcher.fetch_segments
70
171
  end
@@ -87,6 +188,10 @@ module SplitIoClient
87
188
  def start_telemetry_sync_task
88
189
  Telemetry::SyncTask.new(@config, @telemetry_synchronizer).call
89
190
  end
191
+
192
+ def sync_result(success, remaining_attempts, segment_names = nil)
193
+ { success: success, remaining_attempts: remaining_attempts, segment_names: segment_names }
194
+ end
90
195
  end
91
196
  end
92
197
  end
@@ -113,6 +113,9 @@ module SplitIoClient
113
113
 
114
114
  @sdk_start_time = Time.now
115
115
 
116
+ @on_demand_fetch_retry_delay_seconds = SplitConfig.default_on_demand_fetch_retry_delay_seconds
117
+ @on_demand_fetch_max_retries = SplitConfig.default_on_demand_fetch_max_retries
118
+
116
119
  startup_log
117
120
  end
118
121
 
@@ -278,6 +281,17 @@ module SplitIoClient
278
281
 
279
282
  attr_accessor :sdk_start_time
280
283
 
284
+ attr_accessor :on_demand_fetch_retry_delay_seconds
285
+ attr_accessor :on_demand_fetch_max_retries
286
+
287
+ def self.default_on_demand_fetch_retry_delay_seconds
288
+ 0.05
289
+ end
290
+
291
+ def self.default_on_demand_fetch_max_retries
292
+ 10
293
+ end
294
+
281
295
  def self.default_impressions_mode
282
296
  :optimized
283
297
  end
@@ -51,11 +51,7 @@ module SplitIoClient
51
51
  cn = item[:change_number]
52
52
  @config.logger.debug("SegmentsWorker change_number dequeue #{segment_name}, #{cn}")
53
53
 
54
- attempt = 0
55
- while cn > @segments_repository.get_change_number(segment_name).to_i && attempt <= Workers::MAX_RETRIES_ALLOWED
56
- @synchronizer.fetch_segment(segment_name)
57
- attempt += 1
58
- end
54
+ @synchronizer.fetch_segment(segment_name, cn)
59
55
  end
60
56
  end
61
57
 
@@ -3,8 +3,6 @@
3
3
  module SplitIoClient
4
4
  module SSE
5
5
  module Workers
6
- MAX_RETRIES_ALLOWED = 10
7
-
8
6
  class SplitsWorker
9
7
  def initialize(synchronizer, config, splits_repository)
10
8
  @synchronizer = synchronizer
@@ -62,12 +60,7 @@ module SplitIoClient
62
60
  def perform
63
61
  while (change_number = @queue.pop)
64
62
  @config.logger.debug("SplitsWorker change_number dequeue #{change_number}")
65
-
66
- attempt = 0
67
- while change_number > @splits_repository.get_change_number.to_i && attempt <= Workers::MAX_RETRIES_ALLOWED
68
- @synchronizer.fetch_splits
69
- attempt += 1
70
- end
63
+ @synchronizer.fetch_splits(change_number)
71
64
  end
72
65
  end
73
66
 
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '7.3.0'
2
+ VERSION = '7.3.1.pre.rc1'
3
3
  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.3.0
4
+ version: 7.3.1.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: 2021-07-13 00:00:00.000000000 Z
11
+ date: 2021-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -369,6 +369,7 @@ files:
369
369
  - lib/splitclient-rb/engine/api/splits.rb
370
370
  - lib/splitclient-rb/engine/api/telemetry_api.rb
371
371
  - lib/splitclient-rb/engine/auth_api_client.rb
372
+ - lib/splitclient-rb/engine/back_off.rb
372
373
  - lib/splitclient-rb/engine/common/impressions_counter.rb
373
374
  - lib/splitclient-rb/engine/common/impressions_manager.rb
374
375
  - lib/splitclient-rb/engine/evaluator/splitter.rb
@@ -412,7 +413,6 @@ files:
412
413
  - lib/splitclient-rb/split_factory_builder.rb
413
414
  - lib/splitclient-rb/split_factory_registry.rb
414
415
  - lib/splitclient-rb/split_logger.rb
415
- - lib/splitclient-rb/sse/event_source/back_off.rb
416
416
  - lib/splitclient-rb/sse/event_source/client.rb
417
417
  - lib/splitclient-rb/sse/event_source/event_parser.rb
418
418
  - lib/splitclient-rb/sse/event_source/event_types.rb
@@ -465,9 +465,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
465
465
  version: '0'
466
466
  required_rubygems_version: !ruby/object:Gem::Requirement
467
467
  requirements:
468
- - - ">="
468
+ - - ">"
469
469
  - !ruby/object:Gem::Version
470
- version: '0'
470
+ version: 1.3.1
471
471
  requirements: []
472
472
  rubyforge_project:
473
473
  rubygems_version: 2.6.14
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: false
2
-
3
- module SplitIoClient
4
- module SSE
5
- module EventSource
6
- class BackOff
7
- def initialize(back_off_base, attempt = 0)
8
- @attempt = attempt
9
- @back_off_base = back_off_base
10
- end
11
-
12
- def interval
13
- interval = (@back_off_base * (2**@attempt)) if @attempt.positive?
14
- @attempt += 1
15
-
16
- interval || 0
17
- end
18
-
19
- def reset
20
- @attempt = 0
21
- end
22
- end
23
- end
24
- end
25
- end