statsig 2.9.1 → 2.9.2
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/lib/network.rb +9 -0
- data/lib/spec_store.rb +72 -40
- data/lib/statsig.rb +1 -1
- data/lib/statsig_options.rb +9 -1
- 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: cb1b471ac9aa8827bb59d3dd18801a1d7e663e8e3d25ea44be09753f02953ecf
|
|
4
|
+
data.tar.gz: 5d08845dba73c8af7978c38c8519e790361a27dd4ed10f940a8af61d6c1b13e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca761ef81609cbd94bec7294109c893c683477c55c39f6475161269b65a7275e416253d8299e7f1995111d38d7bd7d09687aa8091bb90ffa0762daf609141f85
|
|
7
|
+
data.tar.gz: 29d1482c76a1d78be0fd6e7c80886068a23c1973683050db19a08fa4d6a0b855a2549f24ebbadb6bc391669e09085b0bedc263db76cee9ea736099cf58b5fd80
|
data/lib/network.rb
CHANGED
|
@@ -7,6 +7,7 @@ require 'zlib'
|
|
|
7
7
|
RETRY_CODES = [408, 500, 502, 503, 504, 522, 524, 599].freeze
|
|
8
8
|
|
|
9
9
|
module Statsig
|
|
10
|
+
STATSIG_CDN_DCS_BASE = 'https://api.statsigcdn.com/v2'.freeze
|
|
10
11
|
class NetworkError < StandardError
|
|
11
12
|
attr_reader :http_code
|
|
12
13
|
|
|
@@ -44,6 +45,14 @@ module Statsig
|
|
|
44
45
|
get(dcs_url)
|
|
45
46
|
end
|
|
46
47
|
|
|
48
|
+
def download_config_specs_fallback(since_time, context)
|
|
49
|
+
dcs_url = "#{STATSIG_CDN_DCS_BASE}/download_config_specs/#{@server_secret}.json"
|
|
50
|
+
if since_time.positive?
|
|
51
|
+
dcs_url += "?sinceTime=#{since_time}"
|
|
52
|
+
end
|
|
53
|
+
get(dcs_url)
|
|
54
|
+
end
|
|
55
|
+
|
|
47
56
|
def post_logs(events, error_boundary)
|
|
48
57
|
url = @options.log_event_url
|
|
49
58
|
event_count = events.length
|
data/lib/spec_store.rb
CHANGED
|
@@ -6,6 +6,8 @@ require_relative 'id_list'
|
|
|
6
6
|
|
|
7
7
|
module Statsig
|
|
8
8
|
class SpecStore
|
|
9
|
+
STATSIG_NETWORK_FALLBACK_THRESHOLD = 5
|
|
10
|
+
|
|
9
11
|
attr_accessor :last_config_sync_time
|
|
10
12
|
attr_accessor :initial_config_sync_time
|
|
11
13
|
attr_accessor :init_reason
|
|
@@ -48,6 +50,7 @@ module Statsig
|
|
|
48
50
|
@secret_key = secret_key
|
|
49
51
|
@unsupported_configs = Set.new
|
|
50
52
|
@sdk_configs = sdk_config
|
|
53
|
+
@sync_failure_count = 0
|
|
51
54
|
|
|
52
55
|
startTime = (Time.now.to_f * 1000).to_i
|
|
53
56
|
|
|
@@ -84,6 +87,9 @@ module Statsig
|
|
|
84
87
|
|
|
85
88
|
if @init_reason == EvaluationReason::UNINITIALIZED
|
|
86
89
|
failure_details = download_config_specs('initialize')
|
|
90
|
+
if !failure_details.nil? && @options.fallback_to_statsig_api && using_proxy_for_dcs?
|
|
91
|
+
failure_details = download_config_specs_fallback('initialize')
|
|
92
|
+
end
|
|
87
93
|
end
|
|
88
94
|
|
|
89
95
|
@initial_config_sync_time = @last_config_sync_time == 0 ? -1 : @last_config_sync_time
|
|
@@ -205,10 +211,20 @@ module Statsig
|
|
|
205
211
|
|
|
206
212
|
def sync_config_specs
|
|
207
213
|
if @options.data_store&.should_be_used_for_querying_updates(Interfaces::IDataStore::CONFIG_SPECS_V2_KEY)
|
|
208
|
-
load_config_specs_from_storage_adapter('config_sync')
|
|
214
|
+
failure_details = load_config_specs_from_storage_adapter('config_sync')
|
|
215
|
+
else
|
|
216
|
+
failure_details = download_config_specs('config_sync')
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
if failure_details.nil?
|
|
220
|
+
@sync_failure_count = 0
|
|
209
221
|
else
|
|
210
|
-
|
|
222
|
+
@sync_failure_count += 1
|
|
223
|
+
if @options.fallback_to_statsig_api && using_proxy_for_dcs? && (@sync_failure_count % STATSIG_NETWORK_FALLBACK_THRESHOLD == 0)
|
|
224
|
+
download_config_specs_fallback('config_sync')
|
|
225
|
+
end
|
|
211
226
|
end
|
|
227
|
+
|
|
212
228
|
@logger.log_diagnostics_event(@diagnostics, 'config_sync')
|
|
213
229
|
end
|
|
214
230
|
|
|
@@ -223,6 +239,58 @@ module Statsig
|
|
|
223
239
|
|
|
224
240
|
private
|
|
225
241
|
|
|
242
|
+
def using_proxy_for_dcs?
|
|
243
|
+
!@options.download_config_specs_url.start_with?(STATSIG_CDN_DCS_BASE)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def download_config_specs_fallback(context)
|
|
247
|
+
response, e = @network.download_config_specs_fallback(@last_config_sync_time, context)
|
|
248
|
+
handle_config_specs_response(context, 'download_config_specs_fallback', 'CONFIG_SPECS_FALLBACK', response, e)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def handle_config_specs_response(context, diagnostic_key, error_prefix, response, e)
|
|
252
|
+
tracker = @diagnostics.track(context, diagnostic_key, 'network_request')
|
|
253
|
+
|
|
254
|
+
error = nil
|
|
255
|
+
failure_details = nil
|
|
256
|
+
begin
|
|
257
|
+
code = response&.status.to_i
|
|
258
|
+
if e.is_a? NetworkError
|
|
259
|
+
code = e.http_code
|
|
260
|
+
failure_details = {statusCode: code, exception: e, reason: "#{error_prefix}_NETWORK_ERROR"}
|
|
261
|
+
elsif !e.nil?
|
|
262
|
+
# Transport-level errors (ECONNREFUSED, timeout, DNS, SSL) are caught and returned as values
|
|
263
|
+
# by network.rb. Without this, failure_details stays nil and the caller treats it as success.
|
|
264
|
+
failure_details = {exception: e, reason: "#{error_prefix}_CONNECTION_ERROR"}
|
|
265
|
+
end
|
|
266
|
+
tracker.end(statusCode: code, success: e.nil?, sdkRegion: response&.headers&.[]('X-Statsig-Region'))
|
|
267
|
+
|
|
268
|
+
if e.nil?
|
|
269
|
+
unless response.nil?
|
|
270
|
+
tracker = @diagnostics.track(context, diagnostic_key, 'process')
|
|
271
|
+
body = response.body.to_s
|
|
272
|
+
failure_details = process_specs(body)
|
|
273
|
+
if failure_details.nil?
|
|
274
|
+
@init_reason = EvaluationReason::NETWORK
|
|
275
|
+
end
|
|
276
|
+
tracker.end(success: @init_reason == EvaluationReason::NETWORK)
|
|
277
|
+
|
|
278
|
+
unless response.body.nil? or @rules_updated_callback.nil?
|
|
279
|
+
@rules_updated_callback.call(body, @last_config_sync_time)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
else
|
|
283
|
+
error = e
|
|
284
|
+
end
|
|
285
|
+
rescue StandardError => e
|
|
286
|
+
failure_details = {exception: e, reason: "INTERNAL_ERROR"}
|
|
287
|
+
error = e
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
@error_callback.call(error) unless error.nil? or @error_callback.nil?
|
|
291
|
+
return failure_details
|
|
292
|
+
end
|
|
293
|
+
|
|
226
294
|
def load_config_specs_from_storage_adapter(context)
|
|
227
295
|
tracker = @diagnostics.track(context, 'data_store_config_specs', 'fetch')
|
|
228
296
|
cached_values = @options.data_store.get(Interfaces::IDataStore::CONFIG_SPECS_V2_KEY)
|
|
@@ -284,44 +352,8 @@ module Statsig
|
|
|
284
352
|
end
|
|
285
353
|
|
|
286
354
|
def download_config_specs(context)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
error = nil
|
|
290
|
-
failure_details = nil
|
|
291
|
-
begin
|
|
292
|
-
response, e = @network.download_config_specs(@last_config_sync_time, context)
|
|
293
|
-
code = response&.status.to_i
|
|
294
|
-
if e.is_a? NetworkError
|
|
295
|
-
code = e.http_code
|
|
296
|
-
failure_details = {statusCode: code, exception: e, reason: "CONFIG_SPECS_NETWORK_ERROR"}
|
|
297
|
-
end
|
|
298
|
-
tracker.end(statusCode: code, success: e.nil?, sdkRegion: response&.headers&.[]('X-Statsig-Region'))
|
|
299
|
-
|
|
300
|
-
if e.nil?
|
|
301
|
-
unless response.nil?
|
|
302
|
-
tracker = @diagnostics.track(context, 'download_config_specs', 'process')
|
|
303
|
-
body = response.body.to_s
|
|
304
|
-
failure_details = process_specs(body)
|
|
305
|
-
if failure_details.nil?
|
|
306
|
-
@init_reason = EvaluationReason::NETWORK
|
|
307
|
-
end
|
|
308
|
-
tracker.end(success: @init_reason == EvaluationReason::NETWORK)
|
|
309
|
-
|
|
310
|
-
unless response.body.nil? or @rules_updated_callback.nil?
|
|
311
|
-
@rules_updated_callback.call(body,
|
|
312
|
-
@last_config_sync_time)
|
|
313
|
-
end
|
|
314
|
-
end
|
|
315
|
-
else
|
|
316
|
-
error = e
|
|
317
|
-
end
|
|
318
|
-
rescue StandardError => e
|
|
319
|
-
failure_details = {exception: e, reason: "INTERNAL_ERROR"}
|
|
320
|
-
error = e
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
@error_callback.call(error) unless error.nil? or @error_callback.nil?
|
|
324
|
-
return failure_details
|
|
355
|
+
response, e = @network.download_config_specs(@last_config_sync_time, context)
|
|
356
|
+
handle_config_specs_response(context, 'download_config_specs', 'CONFIG_SPECS', response, e)
|
|
325
357
|
end
|
|
326
358
|
|
|
327
359
|
def process_specs(specs_string, from_adapter: false)
|
data/lib/statsig.rb
CHANGED
data/lib/statsig_options.rb
CHANGED
|
@@ -96,6 +96,12 @@ class StatsigOptions
|
|
|
96
96
|
# default: 0
|
|
97
97
|
attr_accessor :initialize_retry_limit
|
|
98
98
|
|
|
99
|
+
# When true, if the configured download_config_specs_url fails, the SDK will
|
|
100
|
+
# automatically fall back to the Statsig CDN to fetch config specs.
|
|
101
|
+
# Only effective when a custom download_config_specs_url is configured.
|
|
102
|
+
# default: false
|
|
103
|
+
attr_accessor :fallback_to_statsig_api
|
|
104
|
+
|
|
99
105
|
def initialize(
|
|
100
106
|
environment = nil,
|
|
101
107
|
download_config_specs_url: nil,
|
|
@@ -120,7 +126,8 @@ class StatsigOptions
|
|
|
120
126
|
post_logs_retry_backoff: nil,
|
|
121
127
|
user_persistent_storage: nil,
|
|
122
128
|
disable_evaluation_memoization: false,
|
|
123
|
-
initialize_retry_limit: 0
|
|
129
|
+
initialize_retry_limit: 0,
|
|
130
|
+
fallback_to_statsig_api: false
|
|
124
131
|
)
|
|
125
132
|
@environment = environment.is_a?(Hash) ? environment : nil
|
|
126
133
|
|
|
@@ -152,5 +159,6 @@ class StatsigOptions
|
|
|
152
159
|
@user_persistent_storage = user_persistent_storage
|
|
153
160
|
@disable_evaluation_memoization = disable_evaluation_memoization
|
|
154
161
|
@initialize_retry_limit = initialize_retry_limit
|
|
162
|
+
@fallback_to_statsig_api = fallback_to_statsig_api
|
|
155
163
|
end
|
|
156
164
|
end
|
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.9.
|
|
4
|
+
version: 2.9.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Statsig, Inc
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|