statsig 1.2.0 → 1.4.0

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: 90f13c55a79aa919b94e633f69d39b3cb997a92cceec3e27bdb3e00e00e8e3ef
4
- data.tar.gz: cd40c863516da1b3ff0730959ccbc3fa664afe0e613775f0369a1377ccc06123
3
+ metadata.gz: 3ac164a57fadf37c1c9ce2b978df30da43901cc3b53e03e1b3e52a72e19981cb
4
+ data.tar.gz: aad0387cd6a80bfd3cbf501118466f444790ec387b45a96d562a3256331ae034
5
5
  SHA512:
6
- metadata.gz: a4c73b125853c8128d5aa57a60acfe1e80a0aa8ffc9f1fdcd89ea74e9a6ac033c93262103850d0c13ec84052ca9b9a464627836a1af945e6100c036c0fbc1359
7
- data.tar.gz: 86bf2fe76cdc5cdc29126bd188a33e746f2a143e56fce1cd2e7d5a242622acb4b3783694f01e11a6e8ce10c707bc38a57a055694e15c965ee584bb951c6acb0d
6
+ metadata.gz: f7eeefd82a45217becadee9783d2616de5e6997d29236fecea526947a34e54f921125fbf018f57a1fbf2f7560c00d80721f632a9498e0f065b6914b035beef5f
7
+ data.tar.gz: 24b8c55535b7656109b669d898351e536953c83ecc6858e94eabacbdb36f8c9e600b55bb9cd06fc8b043a84305f351e645c07a673a85c1688668de6181c125f3
@@ -9,8 +9,8 @@ class DynamicConfig
9
9
  @rule_id = rule_id
10
10
  end
11
11
 
12
- def get(index)
13
- return nil if @value.nil?
14
- value[index]
12
+ def get(index, default_value)
13
+ return default_value if @value.nil? || !@value.key?(index)
14
+ @value[index]
15
15
  end
16
16
  end
data/lib/evaluator.rb CHANGED
@@ -79,8 +79,7 @@ class Evaluator
79
79
  case type
80
80
  when 'public'
81
81
  return true
82
- when 'fail_gate'
83
- when 'pass_gate'
82
+ when 'fail_gate', 'pass_gate'
84
83
  other_gate_result = self.check_gate(user, target)
85
84
  return $fetch_from_server if other_gate_result == $fetch_from_server
86
85
  return type == 'pass_gate' ? other_gate_result.gate_value : !other_gate_result.gate_value
@@ -109,10 +108,7 @@ class Evaluator
109
108
  return $fetch_from_server
110
109
  end
111
110
 
112
- return $fetch_from_server if value == $fetch_from_server
113
- return false if value.nil?
114
-
115
- return $fetch_from_server unless operator.is_a?(String)
111
+ return $fetch_from_server if value == $fetch_from_server || !operator.is_a?(String)
116
112
  operator = operator.downcase
117
113
 
118
114
  case operator
data/lib/network.rb CHANGED
@@ -2,8 +2,10 @@ require 'http'
2
2
  require 'json'
3
3
  require 'dynamic_config'
4
4
 
5
+ $retry_codes = [408, 500, 502, 503, 504, 522, 524, 599]
6
+
5
7
  class Network
6
- def initialize(server_secret, api)
8
+ def initialize(server_secret, api, backoff_mult = 10)
7
9
  super()
8
10
  unless api.end_with?('/')
9
11
  api += '/'
@@ -11,22 +13,36 @@ class Network
11
13
  @server_secret = server_secret
12
14
  @api = api
13
15
  @last_sync_time = 0
16
+ @backoff_multiplier = backoff_mult
14
17
  end
15
18
 
16
- def post_helper(endpoint, body)
19
+ def post_helper(endpoint, body, retries = 0, backoff = 1)
17
20
  http = HTTP.headers(
18
21
  {"STATSIG-API-KEY" => @server_secret,
19
22
  "STATSIG-CLIENT-TIME" => (Time.now.to_f * 1000).to_s,
20
23
  "Content-Type" => "application/json; charset=UTF-8"
21
24
  }).accept(:json)
22
- http.post(@api + endpoint, body: body)
25
+ begin
26
+ res = http.post(@api + endpoint, body: body)
27
+ rescue
28
+ ## network error retry
29
+ return nil unless retries > 0
30
+ sleep backoff
31
+ return post_helper(endpoint, body, retries - 1, backoff * @backoff_multiplier)
32
+ end
33
+ return res unless !res.status.success?
34
+ return nil unless retries > 0 && $retry_codes.include?(res.code)
35
+ ## status code retry
36
+ sleep backoff
37
+ post_helper(endpoint, body, retries - 1, backoff * @backoff_multiplier)
23
38
  end
24
39
 
25
40
  def check_gate(user, gate_name)
26
41
  begin
27
42
  request_body = JSON.generate({'user' => user&.serialize, 'gateName' => gate_name})
28
43
  response = post_helper('check_gate', request_body)
29
- return JSON.parse(response.body)
44
+ return JSON.parse(response.body) unless response.nil?
45
+ false
30
46
  rescue
31
47
  return false
32
48
  end
@@ -36,7 +52,8 @@ class Network
36
52
  begin
37
53
  request_body = JSON.generate({'user' => user&.serialize, 'configName' => dynamic_config_name})
38
54
  response = post_helper('get_config', request_body)
39
- return JSON.parse(response.body)
55
+ return JSON.parse(response.body) unless response.nil?
56
+ nil
40
57
  rescue
41
58
  return nil
42
59
  end
@@ -45,6 +62,7 @@ class Network
45
62
  def download_config_specs
46
63
  begin
47
64
  response = post_helper('download_config_specs', JSON.generate({'sinceTime' => @last_sync_time}))
65
+ return nil unless !response.nil?
48
66
  json_body = JSON.parse(response.body)
49
67
  @last_sync_time = json_body['time']
50
68
  return json_body
@@ -68,9 +86,8 @@ class Network
68
86
  def post_logs(events, statsig_metadata)
69
87
  begin
70
88
  json_body = JSON.generate({'events' => events, 'statsigMetadata' => statsig_metadata})
71
- post_helper('log_event', body: json_body)
89
+ post_helper('log_event', body: json_body, retries: 5)
72
90
  rescue
73
- # TODO: retries
74
91
  end
75
92
  end
76
93
  end
@@ -105,7 +105,7 @@ class StatsigDriver
105
105
 
106
106
  def shutdown
107
107
  @shutdown = true
108
- @logger.flush
108
+ @logger.flush(true)
109
109
  @polling_thread&.exit
110
110
  end
111
111
 
@@ -8,6 +8,10 @@ class StatsigLogger
8
8
  @network = network
9
9
  @statsig_metadata = statsig_metadata
10
10
  @events = []
11
+ @background_flush = Thread.new do
12
+ sleep 60
13
+ flush
14
+ end
11
15
  end
12
16
 
13
17
  def log_event(event)
@@ -40,13 +44,18 @@ class StatsigLogger
40
44
  log_event(event)
41
45
  end
42
46
 
43
- def flush
47
+ def flush(closing = false)
48
+ if closing
49
+ @background_flush.exit
50
+ end
44
51
  if @events.length == 0
45
52
  return
46
53
  end
47
54
  flush_events = @events.map { |e| e.serialize() }
48
55
  @events = []
49
56
 
50
- @network.post_logs(flush_events, @statsig_metadata)
57
+ Thread.new do
58
+ @network.post_logs(flush_events, @statsig_metadata)
59
+ end
51
60
  end
52
61
  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: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Statsig, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-31 00:00:00.000000000 Z
11
+ date: 2021-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,6 +24,48 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: webmock
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: spy
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: user_agent_parser
29
71
  requirement: !ruby/object:Gem::Requirement