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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5311f827891ea6c944353e8752587e9fe81806d4fef1bc8a44f3f190b6b1538c
4
- data.tar.gz: d6136d7dce23ae5533594513934581ed4ee6aca9325cca24e989dc22057d7293
3
+ metadata.gz: 9feb2cdc643d463427564f33990fba62a3d4049f6361c054c47a37c4668206c4
4
+ data.tar.gz: 17ab60cda06c3c157b0fa8a0d573abf93629a1b7092a5678789e85e044a056ed
5
5
  SHA512:
6
- metadata.gz: dc5efa1c0a0a8d14536685c9ebe9074fa2d705f34bc37729f028b156f7e7c9f8a6e86ea322a55855c6eb434fdf0778a4ddfc548a8c31cc8c7c4bbbc9e929e1d3
7
- data.tar.gz: 0666ebec091e17d2f5321fb46071b05d3872c303857e79747a2d01694573b0c187732f43089a9826c1c3041b7024c89379702c5fc5562debd13a50a6b5cda6b1
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] = clean_exposures(eval_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] = clean_exposures(eval_result.undelegated_sec_exps || [])
134
+ result[:undelegated_secondary_exposures] = eval_result.undelegated_sec_exps || []
147
135
  end
148
136
  end
149
137
 
@@ -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
@@ -363,7 +363,7 @@ module Statsig
363
363
  def self.get_statsig_metadata
364
364
  {
365
365
  'sdkType' => 'ruby-server',
366
- 'sdkVersion' => '1.34.0',
366
+ 'sdkVersion' => '1.34.2',
367
367
  'languageVersion' => RUBY_VERSION
368
368
  }
369
369
  end
@@ -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')
@@ -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.0
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-05-15 00:00:00.000000000 Z
11
+ date: 2024-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler