statsig 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd554df4ba15f0d1addaecc5e76502c1218c1b08842152eeeffd0f6e121dbcba
4
- data.tar.gz: b1d19d91608685488db9705e213559c4a36c21731b0822137b55fab0693a871d
3
+ metadata.gz: 3870c3be99d3f7295c21311a6e938599bbd0ebd260d4b2c2cb814b48d22bd7f4
4
+ data.tar.gz: 618400ee2baa54a9e4bc18ee7e6e1a1cb017349a513919dd5f5e6caa380fb154
5
5
  SHA512:
6
- metadata.gz: b360f7fb18b06e87b538eb005ce0f7344f8a633a1cb9915d3e6f74e5b0fab3b7d87d46ab4b4498548e66766d00a4f60a9bb5f502c270e09925e67c24575da7ca
7
- data.tar.gz: 337e0fd5ecfc11fa5565980df91d21ee17cc6586d49f1a18b333eaa4de1da29094c453a00daf1671ce8e6872d03882b85c169df6709aa41e543500cdc5c78057
6
+ metadata.gz: 47fdd678ef31a026f84bf81a184b39fc171c4d35cb7fb9229ecc2ec835051645cd9ed7b4f6ec2c0bfb92c2b59fe6cd9e742afaff176b438192f6d7b8d07f7ceb
7
+ data.tar.gz: 6db827709c35c1005a22dbf07e81f3ee8d6f316c89ba760beea2ba4adf2a35ae1d5ad2ea97604411733f61e4d167a6ebcbfa1e0dcef05986aacde37aab76dcf0
data/lib/spec_store.rb CHANGED
@@ -44,6 +44,8 @@ module Statsig
44
44
  @secret_key = secret_key
45
45
  @unsupported_configs = Set.new
46
46
 
47
+ startTime = (Time.now.to_f * 1000).to_i
48
+
47
49
  @id_list_thread_pool = Concurrent::FixedThreadPool.new(
48
50
  options.idlist_threadpool_size,
49
51
  name: 'statsig-idlist',
@@ -57,7 +59,7 @@ module Statsig
57
59
  else
58
60
  tracker = @diagnostics.track('initialize','bootstrap', 'process')
59
61
  begin
60
- if process_specs(options.bootstrap_values)
62
+ if process_specs(options.bootstrap_values).nil?
61
63
  @init_reason = EvaluationReason::BOOTSTRAP
62
64
  end
63
65
  rescue StandardError
@@ -68,13 +70,15 @@ module Statsig
68
70
  end
69
71
  end
70
72
 
73
+ failure_details = nil
74
+
71
75
  unless @options.data_store.nil?
72
76
  @options.data_store.init
73
- load_config_specs_from_storage_adapter('initialize')
77
+ failure_details = load_config_specs_from_storage_adapter('initialize')
74
78
  end
75
79
 
76
80
  if @init_reason == EvaluationReason::UNINITIALIZED
77
- download_config_specs('initialize')
81
+ failure_details = download_config_specs('initialize')
78
82
  end
79
83
 
80
84
  @initial_config_sync_time = @last_config_sync_time == 0 ? -1 : @last_config_sync_time
@@ -86,12 +90,18 @@ module Statsig
86
90
 
87
91
  @config_sync_thread = spawn_sync_config_specs_thread
88
92
  @id_lists_sync_thread = spawn_sync_id_lists_thread
93
+ endTime = (Time.now.to_f * 1000).to_i
94
+ @initialization_details = {duration: endTime - startTime, isSDKReady: true, configSpecReady: @init_reason != EvaluationReason::UNINITIALIZED, failureDetails: failure_details}
89
95
  end
90
96
 
91
97
  def is_ready_for_checks
92
98
  @last_config_sync_time != 0
93
99
  end
94
100
 
101
+ def get_initialization_details
102
+ @initialization_details
103
+ end
104
+
95
105
  def shutdown
96
106
  @config_sync_thread&.exit
97
107
  @id_lists_sync_thread&.exit
@@ -202,13 +212,19 @@ module Statsig
202
212
  return if cached_values.nil?
203
213
 
204
214
  tracker = @diagnostics.track(context, 'data_store_config_specs', 'process')
205
- process_specs(cached_values, from_adapter: true)
206
- @init_reason = EvaluationReason::DATA_ADAPTER
207
- tracker.end(success: true)
215
+ failure_details = process_specs(cached_values, from_adapter: true)
216
+ if failure_details.nil?
217
+ @init_reason = EvaluationReason::DATA_ADAPTER
218
+ tracker.end(success: true)
219
+ else
220
+ tracker.end(success: false)
221
+ return download_config_specs(context)
222
+ end
223
+ return failure_details
208
224
  rescue StandardError
209
225
  # Fallback to network
210
226
  tracker.end(success: false)
211
- download_config_specs(context)
227
+ return download_config_specs(context)
212
228
  end
213
229
 
214
230
  def save_rulesets_to_storage_adapter(rulesets_string)
@@ -253,18 +269,21 @@ module Statsig
253
269
  tracker = @diagnostics.track(context, 'download_config_specs', 'network_request')
254
270
 
255
271
  error = nil
272
+ failure_details = nil
256
273
  begin
257
274
  response, e = @network.download_config_specs(@last_config_sync_time)
258
275
  code = response&.status.to_i
259
276
  if e.is_a? NetworkError
260
277
  code = e.http_code
278
+ failure_details = {statusCode: code, exception: e, reason: "CONFIG_SPECS_NETWORK_ERROR"}
261
279
  end
262
280
  tracker.end(statusCode: code, success: e.nil?, sdkRegion: response&.headers&.[]('X-Statsig-Region'))
263
281
 
264
282
  if e.nil?
265
283
  unless response.nil?
266
284
  tracker = @diagnostics.track(context, 'download_config_specs', 'process')
267
- if process_specs(response.body.to_s)
285
+ failure_details = process_specs(response.body.to_s)
286
+ if failure_details.nil?
268
287
  @init_reason = EvaluationReason::NETWORK
269
288
  end
270
289
  tracker.end(success: @init_reason == EvaluationReason::NETWORK)
@@ -274,59 +293,63 @@ module Statsig
274
293
  @last_config_sync_time)
275
294
  end
276
295
  end
277
-
278
- nil
279
296
  else
280
297
  error = e
281
298
  end
282
299
  rescue StandardError => e
300
+ failure_details = {exception: e, reason: "INTERNAL_ERROR"}
283
301
  error = e
284
302
  end
285
303
 
286
304
  @error_callback.call(error) unless error.nil? or @error_callback.nil?
305
+ return failure_details
287
306
  end
288
307
 
289
308
  def process_specs(specs_string, from_adapter: false)
290
309
  if specs_string.nil?
291
- return false
310
+ return {reason: "EMPTY_SPEC"}
292
311
  end
293
312
 
294
- specs_json = JSON.parse(specs_string, { symbolize_names: true })
295
- return false unless specs_json.is_a? Hash
313
+ begin
314
+ specs_json = JSON.parse(specs_string, { symbolize_names: true })
315
+ return {reason: "PARSE_RESPONSE_ERROR"} unless specs_json.is_a? Hash
296
316
 
297
- hashed_sdk_key_used = specs_json[:hashed_sdk_key_used]
298
- unless hashed_sdk_key_used.nil? or hashed_sdk_key_used == Statsig::HashUtils.djb2(@secret_key)
299
- err_boundary.log_exception(Statsig::InvalidSDKKeyResponse.new)
300
- return false
301
- end
317
+ hashed_sdk_key_used = specs_json[:hashed_sdk_key_used]
318
+ unless hashed_sdk_key_used.nil? or hashed_sdk_key_used == Statsig::HashUtils.djb2(@secret_key)
319
+ err_boundary.log_exception(Statsig::InvalidSDKKeyResponse.new)
320
+ return {reason: "PARSE_RESPONSE_ERROR"}
321
+ end
302
322
 
303
- new_specs_sync_time = specs_json[:time]
304
- if new_specs_sync_time.nil? \
305
- || new_specs_sync_time < @last_config_sync_time \
306
- || specs_json[:has_updates] != true \
307
- || specs_json[:feature_gates].nil? \
308
- || specs_json[:dynamic_configs].nil? \
309
- || specs_json[:layer_configs].nil?
310
- return false
311
- end
323
+ new_specs_sync_time = specs_json[:time]
324
+ if new_specs_sync_time.nil? \
325
+ || new_specs_sync_time < @last_config_sync_time \
326
+ || specs_json[:has_updates] != true \
327
+ || specs_json[:feature_gates].nil? \
328
+ || specs_json[:dynamic_configs].nil? \
329
+ || specs_json[:layer_configs].nil?
330
+ return {reason: "PARSE_RESPONSE_ERROR"}
331
+ end
312
332
 
313
- @last_config_sync_time = new_specs_sync_time
314
- @unsupported_configs.clear
333
+ @last_config_sync_time = new_specs_sync_time
334
+ @unsupported_configs.clear
315
335
 
316
- specs_json[:diagnostics]&.each { |key, value| @diagnostics.sample_rates[key.to_s] = value }
336
+ specs_json[:diagnostics]&.each { |key, value| @diagnostics.sample_rates[key.to_s] = value }
317
337
 
318
- @gates = specs_json[:feature_gates]
319
- @configs = specs_json[:dynamic_configs]
320
- @layers = specs_json[:layer_configs]
321
- @condition_map = specs_json[:condition_map]
322
- @experiment_to_layer = specs_json[:experiment_to_layer]
323
- @sdk_keys_to_app_ids = specs_json[:sdk_keys_to_app_ids] || {}
324
- @hashed_sdk_keys_to_app_ids = specs_json[:hashed_sdk_keys_to_app_ids] || {}
338
+ @gates = specs_json[:feature_gates]
339
+ @configs = specs_json[:dynamic_configs]
340
+ @layers = specs_json[:layer_configs]
341
+ @condition_map = specs_json[:condition_map]
342
+ @experiment_to_layer = specs_json[:experiment_to_layer]
343
+ @sdk_keys_to_app_ids = specs_json[:sdk_keys_to_app_ids] || {}
344
+ @hashed_sdk_keys_to_app_ids = specs_json[:hashed_sdk_keys_to_app_ids] || {}
325
345
 
326
- unless from_adapter
327
- save_rulesets_to_storage_adapter(specs_string)
346
+ unless from_adapter
347
+ save_rulesets_to_storage_adapter(specs_string)
348
+ end
349
+ rescue StandardError => e
350
+ return {reason: "PARSE_RESPONSE_ERROR"}
328
351
  end
329
- true
352
+ nil
330
353
  end
331
354
 
332
355
  def get_id_lists_from_adapter(context)
data/lib/statsig.rb CHANGED
@@ -20,6 +20,13 @@ module Statsig
20
20
  @shared_instance = StatsigDriver.new(secret_key, options, error_callback)
21
21
  end
22
22
 
23
+ def self.get_initialization_details
24
+ if not defined? @shared_instance or @shared_instance.nil?
25
+ return {duration: 0, isSDKReady: false, configSpecReady: false, failure_details: {exception: Statsig::UninitializedError.new, reason: 'INTERNAL_ERROR'}}
26
+ end
27
+ @shared_instance.get_initialization_details
28
+ end
29
+
23
30
  class GetGateOptions
24
31
  attr_accessor :disable_log_exposure, :skip_evaluation, :disable_evaluation_details
25
32
 
@@ -65,7 +72,7 @@ module Statsig
65
72
  end
66
73
 
67
74
  ##
68
- # @deprecated - use check_gate(user, gate, options) and disable_exposure_logging in options
75
+ # @deprecated - use check_gate(user, gate, options) with CheckGateOptions.new(disable_log_exposure: true) as options
69
76
  # Gets the boolean result of a gate, evaluated against the given user.
70
77
  #
71
78
  # @param user A StatsigUser object used for the evaluation
@@ -108,7 +115,7 @@ module Statsig
108
115
  end
109
116
 
110
117
  ##
111
- # @deprecated - use get_config(user, config, options) and disable_exposure_logging in options
118
+ # @deprecated - use get_config(user, config, options) with GetConfigOptions.new(disable_log_exposure: true) as options
112
119
  # Get the values of a dynamic config, evaluated against the given user.
113
120
  #
114
121
  # @param [StatsigUser] user A StatsigUser object used for the evaluation
@@ -152,7 +159,7 @@ module Statsig
152
159
  end
153
160
 
154
161
  ##
155
- # @deprecated - use get_experiment(user, experiment, options) and disable_exposure_logging in options
162
+ # @deprecated - use get_experiment(user, experiment, options) with GetExperimentOptions.new(disable_log_exposure: true) as options
156
163
  # Get the values of an experiment, evaluated against the given user.
157
164
  #
158
165
  # @param [StatsigUser] user A StatsigUser object used for the evaluation
@@ -199,7 +206,7 @@ module Statsig
199
206
  end
200
207
 
201
208
  ##
202
- # @deprecated - use get_layer(user, gate, options) and disable_exposure_logging in options
209
+ # @deprecated - use get_layer(user, gate, options) with GetLayerOptions.new(disable_log_exposure: true) as options
203
210
  # Get the values of a layer, evaluated against the given user.
204
211
  #
205
212
  # @param user A StatsigUser object used for the evaluation
@@ -363,7 +370,7 @@ module Statsig
363
370
  def self.get_statsig_metadata
364
371
  {
365
372
  'sdkType' => 'ruby-server',
366
- 'sdkVersion' => '2.1.0',
373
+ 'sdkVersion' => '2.2.0',
367
374
  'languageVersion' => RUBY_VERSION
368
375
  }
369
376
  end
@@ -43,6 +43,10 @@ class StatsigDriver
43
43
  end
44
44
  end
45
45
 
46
+ def get_initialization_details
47
+ @store.get_initialization_details
48
+ end
49
+
46
50
  def get_gate_impl(
47
51
  user,
48
52
  gate_name,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statsig
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Statsig, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-26 00:00:00.000000000 Z
11
+ date: 2024-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler