statsig 1.2.0 → 1.4.0

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: 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