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 +4 -4
- data/lib/spec_store.rb +63 -40
- data/lib/statsig.rb +12 -5
- data/lib/statsig_driver.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3870c3be99d3f7295c21311a6e938599bbd0ebd260d4b2c2cb814b48d22bd7f4
|
4
|
+
data.tar.gz: 618400ee2baa54a9e4bc18ee7e6e1a1cb017349a513919dd5f5e6caa380fb154
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
207
|
-
|
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
|
-
|
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
|
310
|
+
return {reason: "EMPTY_SPEC"}
|
292
311
|
end
|
293
312
|
|
294
|
-
|
295
|
-
|
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
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
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
|
-
|
314
|
-
|
333
|
+
@last_config_sync_time = new_specs_sync_time
|
334
|
+
@unsupported_configs.clear
|
315
335
|
|
316
|
-
|
336
|
+
specs_json[:diagnostics]&.each { |key, value| @diagnostics.sample_rates[key.to_s] = value }
|
317
337
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
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
|
-
|
327
|
-
|
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
|
-
|
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)
|
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)
|
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)
|
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)
|
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.
|
373
|
+
'sdkVersion' => '2.2.0',
|
367
374
|
'languageVersion' => RUBY_VERSION
|
368
375
|
}
|
369
376
|
end
|
data/lib/statsig_driver.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2024-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|