statsig 2.8.4 → 2.9.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 +4 -4
- data/lib/network.rb +86 -39
- data/lib/spec_store.rb +15 -11
- data/lib/statsig.rb +1 -1
- data/lib/statsig_driver.rb +6 -2
- metadata +12 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 800d73e16d593bd9c6b505f9d6207c47e51d8b700344b3c653a07ca8b257cd69
|
|
4
|
+
data.tar.gz: a3cad13252aff42d5eba3e4f9c9b478adb9ec0758633102f2ada9b3d3739052e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f9a1df9789f61b60bb4d015da636b20bf081804b5c1c8d1f542afc3f494538fea68b3bc84ca65d7faf513c63d8d0d72bee94f2583a932c7d54ae0c3cdff560e7
|
|
7
|
+
data.tar.gz: a7d46bfa64e8a52c60eec3bc14c596ff9ffe41493ed68bda741d4171c75da407e7e7842ad63c76addff0c98314d95967b515801efba8f1f5486d8aabc793fc8e
|
data/lib/network.rb
CHANGED
|
@@ -28,26 +28,8 @@ module Statsig
|
|
|
28
28
|
@post_logs_retry_backoff = options.post_logs_retry_backoff
|
|
29
29
|
@post_logs_retry_limit = options.post_logs_retry_limit
|
|
30
30
|
@session_id = SecureRandom.uuid
|
|
31
|
-
@
|
|
32
|
-
|
|
33
|
-
client = HTTP.use(:auto_inflate).headers(
|
|
34
|
-
{
|
|
35
|
-
'STATSIG-API-KEY' => @server_secret,
|
|
36
|
-
'STATSIG-SERVER-SESSION-ID' => @session_id,
|
|
37
|
-
'Content-Type' => 'application/json; charset=UTF-8',
|
|
38
|
-
'STATSIG-SDK-TYPE' => meta['sdkType'],
|
|
39
|
-
'STATSIG-SDK-VERSION' => meta['sdkVersion'],
|
|
40
|
-
'STATSIG-SDK-LANGUAGE-VERSION' => meta['languageVersion'],
|
|
41
|
-
'Accept-Encoding' => 'gzip'
|
|
42
|
-
}
|
|
43
|
-
).accept(:json)
|
|
44
|
-
|
|
45
|
-
if @timeout
|
|
46
|
-
client = client.timeout(@timeout)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
client
|
|
50
|
-
end
|
|
31
|
+
@connection_pools = {}
|
|
32
|
+
@connection_pools_mutex = Mutex.new
|
|
51
33
|
end
|
|
52
34
|
|
|
53
35
|
def download_config_specs(since_time, context)
|
|
@@ -69,10 +51,7 @@ module Statsig
|
|
|
69
51
|
gzip = Zlib::GzipWriter.new(StringIO.new)
|
|
70
52
|
gzip << json_body
|
|
71
53
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# Consume response body to ensure connection can be closed.
|
|
75
|
-
response&.flush
|
|
54
|
+
_response, e = post(url, gzip.close.string, @post_logs_retry_limit, 1, true, event_count)
|
|
76
55
|
|
|
77
56
|
unless e == nil
|
|
78
57
|
message = "Failed to log #{event_count} events after #{@post_logs_retry_limit} retries"
|
|
@@ -92,6 +71,10 @@ module Statsig
|
|
|
92
71
|
post(url, JSON.generate({ 'statsigMetadata' => Statsig.get_statsig_metadata }))
|
|
93
72
|
end
|
|
94
73
|
|
|
74
|
+
def download_id_list(url, start_byte = 0)
|
|
75
|
+
request(:GET, url, nil, 0, 1, false, 0, { 'Range' => "bytes=#{start_byte}-" }, false)
|
|
76
|
+
end
|
|
77
|
+
|
|
95
78
|
def get(url, retries = 0, backoff = 1)
|
|
96
79
|
request(:GET, url, nil, retries, backoff)
|
|
97
80
|
end
|
|
@@ -100,7 +83,21 @@ module Statsig
|
|
|
100
83
|
request(:POST, url, body, retries, backoff, zipped, event_count)
|
|
101
84
|
end
|
|
102
85
|
|
|
103
|
-
def
|
|
86
|
+
def shutdown
|
|
87
|
+
pools = @connection_pools_mutex.synchronize do
|
|
88
|
+
pools = @connection_pools.values
|
|
89
|
+
@connection_pools = {}
|
|
90
|
+
pools
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
pools.each do |pool|
|
|
94
|
+
pool.shutdown do |conn|
|
|
95
|
+
conn.close if conn.respond_to?(:close)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def request(method, url, body, retries = 0, backoff = 1, zipped = false, event_count = 0, extra_headers = {}, use_statsig_headers = true)
|
|
104
101
|
if @local_mode
|
|
105
102
|
return nil, nil
|
|
106
103
|
end
|
|
@@ -114,18 +111,25 @@ module Statsig
|
|
|
114
111
|
end
|
|
115
112
|
|
|
116
113
|
begin
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
114
|
+
pool = connection_pool_for(url)
|
|
115
|
+
res = pool.with do |conn|
|
|
116
|
+
begin
|
|
117
|
+
request_headers = build_request_headers(zipped, event_count, extra_headers, use_statsig_headers)
|
|
118
|
+
request = conn.headers(request_headers)
|
|
119
|
+
|
|
120
|
+
response = case method
|
|
121
|
+
when :GET
|
|
122
|
+
request.get(url)
|
|
123
|
+
when :POST
|
|
124
|
+
request.post(url, body: body)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Fully drain the response before the client returns to the pool.
|
|
128
|
+
response.body.to_s
|
|
129
|
+
response
|
|
130
|
+
rescue StandardError
|
|
131
|
+
pool.discard_current_connection(&:close)
|
|
132
|
+
raise
|
|
129
133
|
end
|
|
130
134
|
end
|
|
131
135
|
rescue StandardError => e
|
|
@@ -133,7 +137,7 @@ module Statsig
|
|
|
133
137
|
return nil, e unless retries.positive?
|
|
134
138
|
|
|
135
139
|
sleep backoff_adjusted
|
|
136
|
-
return request(method, url, body, retries - 1, backoff * @backoff_multiplier, zipped, event_count)
|
|
140
|
+
return request(method, url, body, retries - 1, backoff * @backoff_multiplier, zipped, event_count, extra_headers, use_statsig_headers)
|
|
137
141
|
end
|
|
138
142
|
return res, nil if res.status.success?
|
|
139
143
|
|
|
@@ -144,7 +148,50 @@ module Statsig
|
|
|
144
148
|
|
|
145
149
|
## status code retry
|
|
146
150
|
sleep backoff_adjusted
|
|
147
|
-
request(method, url, body, retries - 1, backoff * @backoff_multiplier, zipped, event_count)
|
|
151
|
+
request(method, url, body, retries - 1, backoff * @backoff_multiplier, zipped, event_count, extra_headers, use_statsig_headers)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def connection_pool_for(url)
|
|
157
|
+
origin = HTTP::URI.parse(url).origin
|
|
158
|
+
@connection_pools_mutex.synchronize do
|
|
159
|
+
@connection_pools[origin] ||= ConnectionPool.new(size: 3) do
|
|
160
|
+
build_persistent_client(origin)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def build_persistent_client(origin)
|
|
166
|
+
client = HTTP.use(:auto_inflate)
|
|
167
|
+
client = client.timeout(@timeout) if @timeout
|
|
168
|
+
client.persistent(origin)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def build_request_headers(zipped, event_count, extra_headers, use_statsig_headers)
|
|
172
|
+
headers = {}
|
|
173
|
+
|
|
174
|
+
if use_statsig_headers
|
|
175
|
+
meta = Statsig.get_statsig_metadata
|
|
176
|
+
headers.merge!(
|
|
177
|
+
'STATSIG-API-KEY' => @server_secret,
|
|
178
|
+
'STATSIG-SERVER-SESSION-ID' => @session_id,
|
|
179
|
+
'Content-Type' => 'application/json; charset=UTF-8',
|
|
180
|
+
'STATSIG-SDK-TYPE' => meta['sdkType'],
|
|
181
|
+
'STATSIG-SDK-VERSION' => meta['sdkVersion'],
|
|
182
|
+
'STATSIG-SDK-LANGUAGE-VERSION' => meta['languageVersion'],
|
|
183
|
+
'Accept' => 'application/json',
|
|
184
|
+
'Accept-Encoding' => 'gzip',
|
|
185
|
+
'STATSIG-CLIENT-TIME' => (Time.now.to_f * 1000).to_i.to_s
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
headers['CONTENT-ENCODING'] = 'gzip' if zipped
|
|
190
|
+
headers['STATSIG-EVENT-COUNT'] = event_count.to_s unless event_count == 0
|
|
191
|
+
|
|
192
|
+
headers.merge!(extra_headers)
|
|
193
|
+
headers.delete_if { |_key, value| value.nil? }
|
|
194
|
+
headers
|
|
148
195
|
end
|
|
149
196
|
end
|
|
150
197
|
end
|
data/lib/spec_store.rb
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
require 'concurrent-ruby'
|
|
2
|
-
require 'net/http'
|
|
3
|
-
require 'uri'
|
|
4
2
|
require_relative 'api_config'
|
|
5
3
|
require_relative 'evaluation_details'
|
|
6
4
|
require_relative 'hash_utils'
|
|
@@ -302,14 +300,15 @@ module Statsig
|
|
|
302
300
|
if e.nil?
|
|
303
301
|
unless response.nil?
|
|
304
302
|
tracker = @diagnostics.track(context, 'download_config_specs', 'process')
|
|
305
|
-
|
|
303
|
+
body = response.body.to_s
|
|
304
|
+
failure_details = process_specs(body)
|
|
306
305
|
if failure_details.nil?
|
|
307
306
|
@init_reason = EvaluationReason::NETWORK
|
|
308
307
|
end
|
|
309
308
|
tracker.end(success: @init_reason == EvaluationReason::NETWORK)
|
|
310
309
|
|
|
311
310
|
unless response.body.nil? or @rules_updated_callback.nil?
|
|
312
|
-
@rules_updated_callback.call(
|
|
311
|
+
@rules_updated_callback.call(body,
|
|
313
312
|
@last_config_sync_time)
|
|
314
313
|
end
|
|
315
314
|
end
|
|
@@ -413,9 +412,10 @@ module Statsig
|
|
|
413
412
|
end
|
|
414
413
|
|
|
415
414
|
begin
|
|
416
|
-
|
|
415
|
+
body = response.body.to_s
|
|
416
|
+
server_id_lists = JSON.parse(body)
|
|
417
417
|
process_id_lists(server_id_lists, context)
|
|
418
|
-
save_id_lists_to_adapter(
|
|
418
|
+
save_id_lists_to_adapter(body)
|
|
419
419
|
rescue StandardError
|
|
420
420
|
# Ignored, will try again
|
|
421
421
|
end
|
|
@@ -509,14 +509,18 @@ module Statsig
|
|
|
509
509
|
|
|
510
510
|
def download_single_id_list(list, context)
|
|
511
511
|
nil unless list.is_a? IDList
|
|
512
|
-
http = HTTP.headers({ 'Range' => "bytes=#{list&.size || 0}-" }).accept(:json)
|
|
513
512
|
tracker = @diagnostics.track(context, 'get_id_list', 'network_request', { url: list.url })
|
|
514
513
|
begin
|
|
515
|
-
res =
|
|
516
|
-
|
|
517
|
-
|
|
514
|
+
res, e = @network.download_id_list(list.url, list&.size || 0)
|
|
515
|
+
code = res&.status&.to_i
|
|
516
|
+
if e.is_a? NetworkError
|
|
517
|
+
code = e.http_code
|
|
518
|
+
end
|
|
519
|
+
success = e.nil? && !res.nil?
|
|
520
|
+
tracker.end(statusCode: code, success: success)
|
|
521
|
+
return nil unless success
|
|
518
522
|
content_length = Integer(res['content-length'])
|
|
519
|
-
nil if content_length.nil? || content_length <= 0
|
|
523
|
+
return nil if content_length.nil? || content_length <= 0
|
|
520
524
|
content = res.body.to_s
|
|
521
525
|
success = process_single_id_list(list, context, content, content_length)
|
|
522
526
|
save_single_id_list_to_adapter(list.name, content) unless success.nil? || !success
|
data/lib/statsig.rb
CHANGED
data/lib/statsig_driver.rb
CHANGED
|
@@ -291,8 +291,12 @@ class StatsigDriver
|
|
|
291
291
|
def shutdown
|
|
292
292
|
@err_boundary.capture(caller: __method__) do
|
|
293
293
|
@shutdown = true
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
begin
|
|
295
|
+
@logger.shutdown
|
|
296
|
+
@evaluator.shutdown
|
|
297
|
+
ensure
|
|
298
|
+
@net.shutdown
|
|
299
|
+
end
|
|
296
300
|
end
|
|
297
301
|
end
|
|
298
302
|
|
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: 2.
|
|
4
|
+
version: 2.9.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: 2026-
|
|
11
|
+
date: 2026-04-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -306,16 +306,22 @@ dependencies:
|
|
|
306
306
|
name: zlib
|
|
307
307
|
requirement: !ruby/object:Gem::Requirement
|
|
308
308
|
requirements:
|
|
309
|
-
- - "
|
|
309
|
+
- - ">="
|
|
310
|
+
- !ruby/object:Gem::Version
|
|
311
|
+
version: '3.1'
|
|
312
|
+
- - "<"
|
|
310
313
|
- !ruby/object:Gem::Version
|
|
311
|
-
version:
|
|
314
|
+
version: '4.0'
|
|
312
315
|
type: :runtime
|
|
313
316
|
prerelease: false
|
|
314
317
|
version_requirements: !ruby/object:Gem::Requirement
|
|
315
318
|
requirements:
|
|
316
|
-
- - "
|
|
319
|
+
- - ">="
|
|
320
|
+
- !ruby/object:Gem::Version
|
|
321
|
+
version: '3.1'
|
|
322
|
+
- - "<"
|
|
317
323
|
- !ruby/object:Gem::Version
|
|
318
|
-
version:
|
|
324
|
+
version: '4.0'
|
|
319
325
|
description: Statsig server SDK for feature gates and experimentation in Ruby
|
|
320
326
|
email: support@statsig.com
|
|
321
327
|
executables: []
|