statsig 1.34.0 → 1.34.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/client_initialize_helpers.rb +2 -12
- 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: 3c2aff544b63f731dd32e2d2a498f83b91b66222f42d507f7ad483ce11b8cadf
|
4
|
+
data.tar.gz: f7c1ec377a7ff7861696cb250817262bf999c8ba81904e6ed663a6e40fee9da0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d42a4f31466ad9598bfa6e9fc59d768fe3821345ff2b1964385fef7a32a480819937994443145bb9b900c6a404611c69a80bc0399fd72c62b726a08a800dd05
|
7
|
+
data.tar.gz: d6b3a2d232962779a8ab5de1d380bc89b28c64ee2ea5ca22c6d140a563cd096f9c5a965de712592d7c72007cf65c9bb853b86e0eaa86c78e49f4207395149759
|
@@ -93,22 +93,12 @@ module Statsig
|
|
93
93
|
result[:rule_id] = eval_result.rule_id
|
94
94
|
|
95
95
|
if include_exposures
|
96
|
-
result[:secondary_exposures] =
|
96
|
+
result[:secondary_exposures] = eval_result.secondary_exposures
|
97
97
|
end
|
98
98
|
|
99
99
|
[hashed_name, result]
|
100
100
|
end
|
101
101
|
|
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
102
|
def self.populate_experiment_fields(config_name, config_spec, eval_result, result, evaluator)
|
113
103
|
result[:is_user_in_experiment] = eval_result.is_experiment_group
|
114
104
|
result[:is_experiment_active] = config_spec.is_active == true
|
@@ -143,7 +133,7 @@ module Statsig
|
|
143
133
|
end
|
144
134
|
|
145
135
|
if include_exposures
|
146
|
-
result[:undelegated_secondary_exposures] =
|
136
|
+
result[:undelegated_secondary_exposures] = eval_result.undelegated_sec_exps || []
|
147
137
|
end
|
148
138
|
end
|
149
139
|
|
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.1
|
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-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|