statsig 1.34.0 → 1.34.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/client_initialize_helpers.rb +8 -20
- data/lib/error_boundary.rb +7 -3
- data/lib/evaluator.rb +33 -9
- data/lib/network.rb +15 -9
- data/lib/statsig.rb +1 -1
- data/lib/statsig_driver.rb +1 -1
- data/lib/statsig_logger.rb +2 -2
- 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: 9feb2cdc643d463427564f33990fba62a3d4049f6361c054c47a37c4668206c4
|
4
|
+
data.tar.gz: 17ab60cda06c3c157b0fa8a0d573abf93629a1b7092a5678789e85e044a056ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df765ca7dd72b88a8c34e2d8844f445a4ad878a0271a2d5f3d1a4410a8b2ce7369753df5acb1ab9cf0556082d9b9ec4618bed53650074083a60bc1f6ebf72757
|
7
|
+
data.tar.gz: ffb48e41016e350a078787fe0392d4d7147e55c8067b0299756b7c26a036c859e648bcac83a56d2f3acc653ac870bfbe57910ef00882efb8bf93157fb905f8cd
|
@@ -14,7 +14,13 @@ module Statsig
|
|
14
14
|
include_local_overrides: false
|
15
15
|
)
|
16
16
|
result = {}
|
17
|
+
target_app_id = evaluator.spec_store.get_app_id_for_sdk_key(client_sdk_key)
|
17
18
|
entities.each do |name, spec|
|
19
|
+
config_target_apps = spec.target_app_ids
|
20
|
+
|
21
|
+
unless target_app_id.nil? || (!config_target_apps.nil? && config_target_apps.include?(target_app_id))
|
22
|
+
next
|
23
|
+
end
|
18
24
|
hashed_name, value = to_response(name, spec, evaluator, user, client_sdk_key, hash_algo, include_exposures, include_local_overrides)
|
19
25
|
if !hashed_name.nil? && !value.nil?
|
20
26
|
result[hashed_name] = value
|
@@ -25,14 +31,6 @@ module Statsig
|
|
25
31
|
end
|
26
32
|
|
27
33
|
def self.to_response(config_name, config_spec, evaluator, user, client_sdk_key, hash_algo, include_exposures, include_local_overrides)
|
28
|
-
|
29
|
-
target_app_id = evaluator.spec_store.get_app_id_for_sdk_key(client_sdk_key)
|
30
|
-
config_target_apps = config_spec.target_app_ids
|
31
|
-
|
32
|
-
unless target_app_id.nil? || (!config_target_apps.nil? && config_target_apps.include?(target_app_id))
|
33
|
-
return nil
|
34
|
-
end
|
35
|
-
|
36
34
|
category = config_spec.type
|
37
35
|
entity_type = config_spec.entity
|
38
36
|
if entity_type == :segment || entity_type == :holdout
|
@@ -93,22 +91,12 @@ module Statsig
|
|
93
91
|
result[:rule_id] = eval_result.rule_id
|
94
92
|
|
95
93
|
if include_exposures
|
96
|
-
result[:secondary_exposures] =
|
94
|
+
result[:secondary_exposures] = eval_result.secondary_exposures
|
97
95
|
end
|
98
96
|
|
99
97
|
[hashed_name, result]
|
100
98
|
end
|
101
99
|
|
102
|
-
def self.clean_exposures(exposures)
|
103
|
-
seen = {}
|
104
|
-
exposures.reject do |exposure|
|
105
|
-
key = "#{exposure[:gate]}|#{exposure[:gateValue]}|#{exposure[:ruleID]}}"
|
106
|
-
should_reject = seen[key]
|
107
|
-
seen[key] = true
|
108
|
-
should_reject == true
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
100
|
def self.populate_experiment_fields(config_name, config_spec, eval_result, result, evaluator)
|
113
101
|
result[:is_user_in_experiment] = eval_result.is_experiment_group
|
114
102
|
result[:is_experiment_active] = config_spec.is_active == true
|
@@ -143,7 +131,7 @@ module Statsig
|
|
143
131
|
end
|
144
132
|
|
145
133
|
if include_exposures
|
146
|
-
result[:undelegated_secondary_exposures] =
|
134
|
+
result[:undelegated_secondary_exposures] = eval_result.undelegated_sec_exps || []
|
147
135
|
end
|
148
136
|
end
|
149
137
|
|
data/lib/error_boundary.rb
CHANGED
@@ -5,9 +5,10 @@ $endpoint = 'https://statsigapi.net/v1/sdk_exception'
|
|
5
5
|
module Statsig
|
6
6
|
class ErrorBoundary
|
7
7
|
|
8
|
-
def initialize(sdk_key)
|
8
|
+
def initialize(sdk_key, local_mode = false)
|
9
9
|
@sdk_key = sdk_key
|
10
10
|
@seen = Set.new
|
11
|
+
@local_mode = local_mode
|
11
12
|
end
|
12
13
|
|
13
14
|
def capture(recover: -> {}, caller: nil)
|
@@ -26,9 +27,12 @@ module Statsig
|
|
26
27
|
return res
|
27
28
|
end
|
28
29
|
|
29
|
-
def log_exception(exception, tag: nil, extra: {})
|
30
|
+
def log_exception(exception, tag: nil, extra: {}, force: false)
|
31
|
+
if @local_mode
|
32
|
+
return
|
33
|
+
end
|
30
34
|
name = exception.class.name
|
31
|
-
if @seen.include?(name)
|
35
|
+
if @seen.include?(name) && !force
|
32
36
|
return
|
33
37
|
end
|
34
38
|
|
data/lib/evaluator.rb
CHANGED
@@ -71,7 +71,7 @@ module Statsig
|
|
71
71
|
return nil
|
72
72
|
end
|
73
73
|
|
74
|
-
def check_gate(user, gate_name, end_result, ignore_local_overrides: false)
|
74
|
+
def check_gate(user, gate_name, end_result, ignore_local_overrides: false, is_nested: false)
|
75
75
|
unless ignore_local_overrides
|
76
76
|
local_override = lookup_gate_override(gate_name)
|
77
77
|
unless local_override.nil?
|
@@ -96,7 +96,7 @@ module Statsig
|
|
96
96
|
return
|
97
97
|
end
|
98
98
|
|
99
|
-
eval_spec(user, @spec_store.get_gate(gate_name), end_result)
|
99
|
+
eval_spec(user, @spec_store.get_gate(gate_name), end_result, is_nested: is_nested)
|
100
100
|
end
|
101
101
|
|
102
102
|
def get_config(user, config_name, end_result, user_persisted_values: nil, ignore_local_overrides: false)
|
@@ -281,9 +281,9 @@ module Statsig
|
|
281
281
|
@config_overrides.clear
|
282
282
|
end
|
283
283
|
|
284
|
-
def eval_spec(user, config, end_result)
|
284
|
+
def eval_spec(user, config, end_result, is_nested: false)
|
285
285
|
unless config.enabled
|
286
|
-
finalize_eval_result(config, end_result, did_pass: false, rule: nil)
|
286
|
+
finalize_eval_result(config, end_result, did_pass: false, rule: nil, is_nested: is_nested)
|
287
287
|
return
|
288
288
|
end
|
289
289
|
|
@@ -292,21 +292,22 @@ module Statsig
|
|
292
292
|
|
293
293
|
if end_result.gate_value
|
294
294
|
if eval_delegate(config.name, user, rule, end_result)
|
295
|
+
finalize_secondary_exposures(end_result)
|
295
296
|
return
|
296
297
|
end
|
297
298
|
|
298
299
|
pass = eval_pass_percent(user, rule, config.salt)
|
299
|
-
finalize_eval_result(config, end_result, did_pass: pass, rule: rule)
|
300
|
+
finalize_eval_result(config, end_result, did_pass: pass, rule: rule, is_nested: is_nested)
|
300
301
|
return
|
301
302
|
end
|
302
303
|
end
|
303
304
|
|
304
|
-
finalize_eval_result(config, end_result, did_pass: false, rule: nil)
|
305
|
+
finalize_eval_result(config, end_result, did_pass: false, rule: nil, is_nested: is_nested)
|
305
306
|
end
|
306
307
|
|
307
308
|
private
|
308
309
|
|
309
|
-
def finalize_eval_result(config, end_result, did_pass:, rule:)
|
310
|
+
def finalize_eval_result(config, end_result, did_pass:, rule:, is_nested: false)
|
310
311
|
end_result.id_type = config.id_type
|
311
312
|
end_result.target_app_ids = config.target_app_ids
|
312
313
|
end_result.gate_value = did_pass
|
@@ -330,6 +331,29 @@ module Statsig
|
|
330
331
|
@spec_store.init_reason
|
331
332
|
)
|
332
333
|
end
|
334
|
+
|
335
|
+
unless is_nested
|
336
|
+
finalize_secondary_exposures(end_result)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def finalize_secondary_exposures(end_result)
|
341
|
+
end_result.secondary_exposures = clean_exposures(end_result.secondary_exposures)
|
342
|
+
end_result.undelegated_sec_exps = clean_exposures(end_result.undelegated_sec_exps)
|
343
|
+
end
|
344
|
+
|
345
|
+
def clean_exposures(exposures)
|
346
|
+
seen = {}
|
347
|
+
exposures.reject do |exposure|
|
348
|
+
if exposure[:gate].to_s.start_with?('segment:')
|
349
|
+
should_reject = true
|
350
|
+
else
|
351
|
+
key = "#{exposure[:gate]}|#{exposure[:gateValue]}|#{exposure[:ruleID]}}"
|
352
|
+
should_reject = seen[key]
|
353
|
+
seen[key] = true
|
354
|
+
end
|
355
|
+
should_reject == true
|
356
|
+
end
|
333
357
|
end
|
334
358
|
|
335
359
|
def eval_rule(user, rule, end_result)
|
@@ -362,7 +386,7 @@ module Statsig
|
|
362
386
|
|
363
387
|
end_result.undelegated_sec_exps = end_result.secondary_exposures.dup
|
364
388
|
|
365
|
-
eval_spec(user, config, end_result)
|
389
|
+
eval_spec(user, config, end_result, is_nested: true)
|
366
390
|
|
367
391
|
end_result.name = name
|
368
392
|
end_result.config_delegate = delegate
|
@@ -384,7 +408,7 @@ module Statsig
|
|
384
408
|
when :public
|
385
409
|
return true
|
386
410
|
when :fail_gate, :pass_gate
|
387
|
-
check_gate(user, target, end_result)
|
411
|
+
check_gate(user, target, end_result, is_nested: true)
|
388
412
|
gate_value = end_result.gate_value
|
389
413
|
|
390
414
|
unless end_result.disable_exposures
|
data/lib/network.rb
CHANGED
@@ -59,15 +59,14 @@ module Statsig
|
|
59
59
|
request(:GET, endpoint, nil, retries, backoff)
|
60
60
|
end
|
61
61
|
|
62
|
-
def post(endpoint, body, retries = 0, backoff = 1, zipped = false)
|
63
|
-
request(:POST, endpoint, body, retries, backoff, zipped)
|
62
|
+
def post(endpoint, body, retries = 0, backoff = 1, zipped = false, event_count = 0)
|
63
|
+
request(:POST, endpoint, body, retries, backoff, zipped, event_count)
|
64
64
|
end
|
65
65
|
|
66
|
-
def request(method, endpoint, body, retries = 0, backoff = 1, zipped = false)
|
66
|
+
def request(method, endpoint, body, retries = 0, backoff = 1, zipped = false, event_count = 0)
|
67
67
|
if @local_mode
|
68
68
|
return nil, nil
|
69
69
|
end
|
70
|
-
|
71
70
|
backoff_adjusted = backoff > 10 ? backoff += Random.rand(10) : backoff # to deter overlap
|
72
71
|
if @post_logs_retry_backoff
|
73
72
|
if @post_logs_retry_backoff.is_a? Integer
|
@@ -79,7 +78,7 @@ module Statsig
|
|
79
78
|
url = URIHelper.build_url(endpoint)
|
80
79
|
begin
|
81
80
|
res = @connection_pool.with do |conn|
|
82
|
-
request = conn.headers('STATSIG-CLIENT-TIME' => (Time.now.to_f * 1000).to_i.to_s, 'CONTENT-ENCODING' => zipped ? 'gzip' : nil)
|
81
|
+
request = conn.headers('STATSIG-CLIENT-TIME' => (Time.now.to_f * 1000).to_i.to_s, 'CONTENT-ENCODING' => zipped ? 'gzip' : nil, 'STATSIG-EVENT-COUNT' => event_count == 0 ? nil : event_count.to_s)
|
83
82
|
case method
|
84
83
|
when :GET
|
85
84
|
request.get(url)
|
@@ -92,7 +91,7 @@ module Statsig
|
|
92
91
|
return nil, e unless retries.positive?
|
93
92
|
|
94
93
|
sleep backoff_adjusted
|
95
|
-
return request(method, endpoint, body, retries - 1, backoff * @backoff_multiplier, zipped)
|
94
|
+
return request(method, endpoint, body, retries - 1, backoff * @backoff_multiplier, zipped, event_count)
|
96
95
|
end
|
97
96
|
return res, nil if res.status.success?
|
98
97
|
|
@@ -103,14 +102,21 @@ module Statsig
|
|
103
102
|
|
104
103
|
## status code retry
|
105
104
|
sleep backoff_adjusted
|
106
|
-
request(method, endpoint, body, retries - 1, backoff * @backoff_multiplier, zipped)
|
105
|
+
request(method, endpoint, body, retries - 1, backoff * @backoff_multiplier, zipped, event_count)
|
107
106
|
end
|
108
107
|
|
109
|
-
def post_logs(events)
|
108
|
+
def post_logs(events, error_boundary)
|
109
|
+
event_count = events.length
|
110
110
|
json_body = JSON.generate({ events: events, statsigMetadata: Statsig.get_statsig_metadata })
|
111
111
|
gzip = Zlib::GzipWriter.new(StringIO.new)
|
112
112
|
gzip << json_body
|
113
|
-
post('log_event', gzip.close.string, @post_logs_retry_limit, 1, true)
|
113
|
+
response, e = post('log_event', gzip.close.string, @post_logs_retry_limit, 1, true, event_count)
|
114
|
+
unless e == nil
|
115
|
+
message = "Failed to log #{event_count} events after #{@post_logs_retry_limit} retries"
|
116
|
+
puts "[Statsig]: #{message}"
|
117
|
+
error_boundary.log_exception(e, tag: 'statsig::log_event_failed', extra: { eventCount: event_count, error: message }, force: true)
|
118
|
+
return
|
119
|
+
end
|
114
120
|
rescue StandardError
|
115
121
|
|
116
122
|
end
|
data/lib/statsig.rb
CHANGED
data/lib/statsig_driver.rb
CHANGED
@@ -25,7 +25,7 @@ class StatsigDriver
|
|
25
25
|
raise Statsig::ValueError.new('Invalid options provided. Either provide a valid StatsigOptions object or nil')
|
26
26
|
end
|
27
27
|
|
28
|
-
@err_boundary = Statsig::ErrorBoundary.new(secret_key)
|
28
|
+
@err_boundary = Statsig::ErrorBoundary.new(secret_key, !options.nil? && options.local_mode)
|
29
29
|
@err_boundary.capture(caller: __method__) do
|
30
30
|
@diagnostics = Statsig::Diagnostics.new()
|
31
31
|
tracker = @diagnostics.track('initialize', 'overall')
|
data/lib/statsig_logger.rb
CHANGED
@@ -160,7 +160,7 @@ module Statsig
|
|
160
160
|
events_clone = @events
|
161
161
|
@events = []
|
162
162
|
flush_events = events_clone.map { |e| e.serialize }
|
163
|
-
@network.post_logs(flush_events)
|
163
|
+
@network.post_logs(flush_events, @error_boundary)
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
@@ -207,7 +207,7 @@ module Statsig
|
|
207
207
|
if metadata.is_a?(Hash)
|
208
208
|
metadata_key = metadata.reject { |key, _| $ignored_metadata_keys.include?(key) }.values.join(',')
|
209
209
|
end
|
210
|
-
|
210
|
+
|
211
211
|
key = [user_key, event_name, metadata_key].join(',')
|
212
212
|
|
213
213
|
return false if @deduper.include?(key)
|
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: 1.34.
|
4
|
+
version: 1.34.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: 2024-
|
11
|
+
date: 2024-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|