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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5311f827891ea6c944353e8752587e9fe81806d4fef1bc8a44f3f190b6b1538c
4
- data.tar.gz: d6136d7dce23ae5533594513934581ed4ee6aca9325cca24e989dc22057d7293
3
+ metadata.gz: 3c2aff544b63f731dd32e2d2a498f83b91b66222f42d507f7ad483ce11b8cadf
4
+ data.tar.gz: f7c1ec377a7ff7861696cb250817262bf999c8ba81904e6ed663a6e40fee9da0
5
5
  SHA512:
6
- metadata.gz: dc5efa1c0a0a8d14536685c9ebe9074fa2d705f34bc37729f028b156f7e7c9f8a6e86ea322a55855c6eb434fdf0778a4ddfc548a8c31cc8c7c4bbbc9e929e1d3
7
- data.tar.gz: 0666ebec091e17d2f5321fb46071b05d3872c303857e79747a2d01694573b0c187732f43089a9826c1c3041b7024c89379702c5fc5562debd13a50a6b5cda6b1
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] = clean_exposures(eval_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] = clean_exposures(eval_result.undelegated_sec_exps || [])
136
+ result[:undelegated_secondary_exposures] = eval_result.undelegated_sec_exps || []
147
137
  end
148
138
  end
149
139
 
@@ -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.1',
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.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-05-15 00:00:00.000000000 Z
11
+ date: 2024-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler