statsig 1.14.0 → 1.16.1

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: 2536756538598d9f75c2b8920c90b7be1ab91ab05b416aedb5b05f325a6f5fde
4
- data.tar.gz: 045e5cc822c2355b5fd4c1cb9386425df3d85930c3f56735f96816b2242ae3a7
3
+ metadata.gz: 4dcb84e4bc8ba66021edba175badb275faf6adfd25ca69f662a8b66a1c1cee64
4
+ data.tar.gz: e4564a84d37ce348006eddbc6e7ff6371e1493e0a4e13e9ad987653b1f0b266a
5
5
  SHA512:
6
- metadata.gz: 39494460ee50c89666dad6962a023dc7a954a24d386621855d219789a749e8ae58c40f19f4c8f472c46e5d3df33e28d5e3e05b9e44ccbf6569560353ae34670c
7
- data.tar.gz: 24b455140fcd7a4d628ce3780d19ee0ce7824fe41b799f39a015c8cf98f5124796d060f27ff65b353c0c9895fbadb6add845c2b09a3b0d3b2d68c3c8810a30be
6
+ metadata.gz: 7c33220e646b1af64499f40989ac4bccffbaf1cbd352f3ab6efc13a0d5246e726a37b95f2bce2c912fcb7b3fb8ed67d487ff9da07f28d01917178cfd8fba2293
7
+ data.tar.gz: 2ea4f85caede773fc57f84934946f3fde39002d524326e9134ed2cc3410ed02258034b3a81808bc438c7a6d200dfeb5268c16c57a36bcbdce1956424257e1141
@@ -6,6 +6,7 @@ module Statsig
6
6
  UNRECOGNIZED = "Unrecognized"
7
7
  UNINITIALIZED = "Uninitialized"
8
8
  BOOTSTRAP = "Bootstrap"
9
+ DATA_ADAPTER = "DataAdapter"
9
10
  end
10
11
 
11
12
  class EvaluationDetails
data/lib/evaluator.rb CHANGED
@@ -17,7 +17,7 @@ module Statsig
17
17
  attr_accessor :spec_store
18
18
 
19
19
  def initialize(network, options, error_callback)
20
- @spec_store = Statsig::SpecStore.new(network, error_callback, options.rulesets_sync_interval, options.idlists_sync_interval, options.bootstrap_values, options.rules_updated_callback)
20
+ @spec_store = Statsig::SpecStore.new(network, options, error_callback)
21
21
  @ua_parser = UserAgentParser::Parser.new
22
22
  CountryLookup.initialize
23
23
 
@@ -0,0 +1,18 @@
1
+ module Statsig
2
+ module Interfaces
3
+ class IDataStore
4
+ def init
5
+ end
6
+
7
+ def get(key)
8
+ nil
9
+ end
10
+
11
+ def set(key, value)
12
+ end
13
+
14
+ def shutdown
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/network.rb CHANGED
@@ -19,7 +19,9 @@ module Statsig
19
19
  end
20
20
 
21
21
  def post_helper(endpoint, body, retries = 0, backoff = 1)
22
- return nil unless !@local_mode
22
+ if @local_mode
23
+ return nil, nil
24
+ end
23
25
  http = HTTP.headers(
24
26
  {"STATSIG-API-KEY" => @server_secret,
25
27
  "STATSIG-CLIENT-TIME" => (Time.now.to_f * 1000).to_i.to_s,
data/lib/spec_store.rb CHANGED
@@ -1,23 +1,27 @@
1
1
  require 'net/http'
2
2
  require 'uri'
3
-
4
3
  require 'evaluation_details'
5
4
  require 'id_list'
6
5
 
7
6
  module Statsig
8
7
  class SpecStore
8
+
9
+ CONFIG_SPECS_KEY = "statsig.cache"
10
+
9
11
  attr_accessor :last_config_sync_time
10
12
  attr_accessor :initial_config_sync_time
11
13
  attr_accessor :init_reason
12
14
 
13
- def initialize(network, error_callback = nil, rulesets_sync_interval = 10, id_lists_sync_interval = 60, bootstrap_values = nil, rules_updated_callback = nil)
15
+ def initialize(network, options, error_callback)
14
16
  @init_reason = EvaluationReason::UNINITIALIZED
15
17
  @network = network
18
+ @options = options
19
+ @error_callback = error_callback
16
20
  @last_config_sync_time = 0
17
21
  @initial_config_sync_time = 0
18
- @rulesets_sync_interval = rulesets_sync_interval
19
- @id_lists_sync_interval = id_lists_sync_interval
20
- @rules_updated_callback = rules_updated_callback
22
+ @rulesets_sync_interval = options.rulesets_sync_interval
23
+ @id_lists_sync_interval = options.idlists_sync_interval
24
+ @rules_updated_callback = options.rules_updated_callback
21
25
  @specs = {
22
26
  :gates => {},
23
27
  :configs => {},
@@ -26,9 +30,11 @@ module Statsig
26
30
  :experiment_to_layer => {}
27
31
  }
28
32
 
29
- unless bootstrap_values.nil?
33
+ unless @options.bootstrap_values.nil?
30
34
  begin
31
- if process(JSON.parse(bootstrap_values))
35
+ if !@options.data_store.nil?
36
+ puts 'data_store gets priority over bootstrap_values. bootstrap_values will be ignored'
37
+ elsif process(options.bootstrap_values)
32
38
  @init_reason = EvaluationReason::BOOTSTRAP
33
39
  end
34
40
  rescue
@@ -36,7 +42,11 @@ module Statsig
36
42
  end
37
43
  end
38
44
 
39
- @error_callback = error_callback
45
+ unless @options.data_store.nil?
46
+ @options.data_store.init
47
+ load_from_storage_adapter
48
+ end
49
+
40
50
  download_config_specs
41
51
  @initial_config_sync_time = @last_config_sync_time == 0 ? -1 : @last_config_sync_time
42
52
  get_id_lists
@@ -52,6 +62,9 @@ module Statsig
52
62
  def shutdown
53
63
  @config_sync_thread&.exit
54
64
  @id_lists_sync_thread&.exit
65
+ unless @options.data_store.nil?
66
+ @options.data_store.shutdown
67
+ end
55
68
  end
56
69
 
57
70
  def has_gate?(gate_name)
@@ -100,10 +113,26 @@ module Statsig
100
113
 
101
114
  private
102
115
 
116
+ def load_from_storage_adapter
117
+ cached_values = @options.data_store.get(CONFIG_SPECS_KEY)
118
+ if cached_values.nil?
119
+ return
120
+ end
121
+ process(cached_values, true)
122
+ @init_reason = EvaluationReason::DATA_ADAPTER
123
+ end
124
+
125
+ def save_to_storage_adapter(specs_string)
126
+ if @options.data_store.nil?
127
+ return
128
+ end
129
+ @options.data_store.set(CONFIG_SPECS_KEY, specs_string)
130
+ end
131
+
103
132
  def sync_config_specs
104
133
  Thread.new do
105
134
  loop do
106
- sleep @rulesets_sync_interval
135
+ sleep @options.rulesets_sync_interval
107
136
  download_config_specs
108
137
  end
109
138
  end
@@ -127,7 +156,7 @@ module Statsig
127
156
  begin
128
157
  response, e = @network.post_helper('download_config_specs', JSON.generate({ 'sinceTime' => @last_config_sync_time }))
129
158
  if e.nil?
130
- if process(JSON.parse(response.body))
159
+ if !response.nil? and process(response.body)
131
160
  @init_reason = EvaluationReason::NETWORK
132
161
  @rules_updated_callback.call(response.body.to_s, @last_config_sync_time) unless response.body.nil? or @rules_updated_callback.nil?
133
162
  end
@@ -140,13 +169,15 @@ module Statsig
140
169
  end
141
170
  end
142
171
 
143
- def process(specs_json)
144
- if specs_json.nil?
172
+ def process(specs_string, from_adapter = false)
173
+ if specs_string.nil?
145
174
  return false
146
175
  end
147
176
 
148
- @last_config_sync_time = specs_json['time'] || @last_config_sync_time
177
+ specs_json = JSON.parse(specs_string)
178
+ return false unless specs_json.is_a? Hash
149
179
 
180
+ @last_config_sync_time = specs_json['time'] || @last_config_sync_time
150
181
  return false unless specs_json['has_updates'] == true &&
151
182
  !specs_json['feature_gates'].nil? &&
152
183
  !specs_json['dynamic_configs'].nil? &&
@@ -171,6 +202,10 @@ module Statsig
171
202
  @specs[:configs] = new_configs
172
203
  @specs[:layers] = new_layers
173
204
  @specs[:experiment_to_layer] = new_exp_to_layer
205
+
206
+ unless from_adapter
207
+ save_to_storage_adapter(specs_string)
208
+ end
174
209
  true
175
210
  end
176
211
 
@@ -253,7 +288,7 @@ module Statsig
253
288
  line = li.strip
254
289
  next if line.length <= 1
255
290
  op = line[0]
256
- id = line[1..]
291
+ id = line[1..line.length]
257
292
  if op == '+'
258
293
  ids_clone.add(id)
259
294
  elsif op == '-'
data/lib/statsig.rb CHANGED
@@ -61,7 +61,7 @@ module Statsig
61
61
  def self.get_statsig_metadata
62
62
  {
63
63
  'sdkType' => 'ruby-server',
64
- 'sdkVersion' => '1.14.0',
64
+ 'sdkVersion' => '1.16.1',
65
65
  }
66
66
  end
67
67
 
@@ -94,7 +94,7 @@ class StatsigDriver
94
94
 
95
95
  def shutdown
96
96
  @shutdown = true
97
- @logger.flush(true)
97
+ @logger.shutdown
98
98
  @evaluator.shutdown
99
99
  end
100
100
 
@@ -115,7 +115,6 @@ class StatsigDriver
115
115
 
116
116
  def maybe_restart_background_threads
117
117
  @evaluator.maybe_restart_background_threads
118
- @logger.maybe_restart_background_threads
119
118
  end
120
119
 
121
120
  private
data/lib/statsig_event.rb CHANGED
@@ -7,6 +7,10 @@ class StatsigEvent
7
7
 
8
8
  def initialize(event_name)
9
9
  @event_name = event_name
10
+ @value = nil
11
+ @metadata = nil
12
+ @secondary_exposures = nil
13
+ @user = nil
10
14
  @time = (Time.now.to_f * 1000).to_i
11
15
  end
12
16
 
@@ -1,4 +1,5 @@
1
1
  require 'statsig_event'
2
+ require 'concurrent-ruby'
2
3
 
3
4
  $gate_exposure_event = 'statsig::gate_exposure'
4
5
  $config_exposure_event = 'statsig::config_exposure'
@@ -9,14 +10,23 @@ module Statsig
9
10
  def initialize(network, options)
10
11
  @network = network
11
12
  @events = []
12
- @background_flush = periodic_flush
13
13
  @options = options
14
+
15
+ @logging_pool = Concurrent::ThreadPoolExecutor.new(
16
+ min_threads: [2, Concurrent.processor_count].min,
17
+ max_threads: [2, Concurrent.processor_count].max,
18
+ # max jobs pending before we start dropping
19
+ max_queue: [2, Concurrent.processor_count].max * 5,
20
+ fallback_policy: :discard,
21
+ )
22
+
23
+ @background_flush = periodic_flush
14
24
  end
15
25
 
16
26
  def log_event(event)
17
27
  @events.push(event)
18
28
  if @events.length >= @options.logging_max_buffer_size
19
- flush
29
+ flush_async
20
30
  end
21
31
  end
22
32
 
@@ -83,10 +93,20 @@ module Statsig
83
93
  end
84
94
  end
85
95
 
86
- def flush(closing = false)
87
- if closing
88
- @background_flush&.exit
96
+ def shutdown
97
+ @background_flush&.exit
98
+ @logging_pool.shutdown
99
+ @logging_pool.wait_for_termination(timeout = 3)
100
+ flush
101
+ end
102
+
103
+ def flush_async
104
+ @logging_pool.post do
105
+ flush
89
106
  end
107
+ end
108
+
109
+ def flush
90
110
  if @events.length == 0
91
111
  return
92
112
  end
@@ -94,19 +114,7 @@ module Statsig
94
114
  @events = []
95
115
  flush_events = events_clone.map { |e| e.serialize }
96
116
 
97
- if closing
98
- @network.post_logs(flush_events)
99
- else
100
- Thread.new do
101
- @network.post_logs(flush_events)
102
- end
103
- end
104
- end
105
-
106
- def maybe_restart_background_threads
107
- if @background_flush.nil? or !@background_flush.alive?
108
- @background_flush = periodic_flush
109
- end
117
+ @network.post_logs(flush_events)
110
118
  end
111
119
 
112
120
  private
@@ -8,6 +8,7 @@ class StatsigOptions
8
8
  attr_accessor :local_mode
9
9
  attr_accessor :bootstrap_values
10
10
  attr_accessor :rules_updated_callback
11
+ attr_accessor :data_store
11
12
 
12
13
  def initialize(
13
14
  environment=nil,
@@ -18,7 +19,8 @@ class StatsigOptions
18
19
  logging_max_buffer_size: 1000,
19
20
  local_mode: false,
20
21
  bootstrap_values: nil,
21
- rules_updated_callback: nil)
22
+ rules_updated_callback: nil,
23
+ data_store: nil)
22
24
  @environment = environment.is_a?(Hash) ? environment : nil
23
25
  @api_url_base = api_url_base
24
26
  @rulesets_sync_interval = rulesets_sync_interval
@@ -28,5 +30,6 @@ class StatsigOptions
28
30
  @local_mode = local_mode
29
31
  @bootstrap_values = bootstrap_values
30
32
  @rules_updated_callback = rules_updated_callback
33
+ @data_store = data_store
31
34
  end
32
35
  end
data/lib/statsig_user.rb CHANGED
@@ -19,6 +19,16 @@ class StatsigUser
19
19
  end
20
20
 
21
21
  def initialize(user_hash)
22
+ @user_id = nil
23
+ @email = nil
24
+ @ip = nil
25
+ @user_agent = nil
26
+ @country = nil
27
+ @locale = nil
28
+ @app_version = nil
29
+ @custom = nil
30
+ @private_attributes = nil
31
+ @custom_ids = nil
22
32
  @statsig_environment = Hash.new
23
33
  if user_hash.is_a?(Hash)
24
34
  @user_id = user_hash['userID'] || user_hash['user_id']
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.14.0
4
+ version: 1.16.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: 2022-09-27 00:00:00.000000000 Z
11
+ date: 2022-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.1'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.1'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: webmock
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -102,18 +102,32 @@ dependencies:
102
102
  version: '6.0'
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: ip3country
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 0.1.1
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - '='
115
+ - !ruby/object:Gem::Version
116
+ version: 0.1.1
117
+ - !ruby/object:Gem::Dependency
118
+ name: concurrent-ruby
105
119
  requirement: !ruby/object:Gem::Requirement
106
120
  requirements:
107
121
  - - "~>"
108
122
  - !ruby/object:Gem::Version
109
- version: '0.1'
123
+ version: '1.1'
110
124
  type: :runtime
111
125
  prerelease: false
112
126
  version_requirements: !ruby/object:Gem::Requirement
113
127
  requirements:
114
128
  - - "~>"
115
129
  - !ruby/object:Gem::Version
116
- version: '0.1'
130
+ version: '1.1'
117
131
  description: Statsig server SDK for feature gates and experimentation in Ruby
118
132
  email: support@statsig.com
119
133
  executables: []
@@ -127,6 +141,7 @@ files:
127
141
  - lib/evaluation_helpers.rb
128
142
  - lib/evaluator.rb
129
143
  - lib/id_list.rb
144
+ - lib/interfaces/data_store.rb
130
145
  - lib/layer.rb
131
146
  - lib/network.rb
132
147
  - lib/spec_store.rb
@@ -148,14 +163,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
163
  requirements:
149
164
  - - ">="
150
165
  - !ruby/object:Gem::Version
151
- version: '0'
166
+ version: 2.5.0
152
167
  required_rubygems_version: !ruby/object:Gem::Requirement
153
168
  requirements:
154
169
  - - ">="
155
170
  - !ruby/object:Gem::Version
156
171
  version: '0'
157
172
  requirements: []
158
- rubygems_version: 3.3.11
173
+ rubygems_version: 3.3.22
159
174
  signing_key:
160
175
  specification_version: 4
161
176
  summary: Statsig server SDK for Ruby