statsig 1.31.1 → 1.32.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/api_config.rb +128 -0
- data/lib/client_initialize_helpers.rb +78 -88
- data/lib/config_result.rb +17 -32
- data/lib/constants.rb +60 -0
- data/lib/diagnostics.rb +1 -38
- data/lib/dynamic_config.rb +1 -24
- data/lib/error_boundary.rb +0 -5
- data/lib/evaluation_details.rb +4 -1
- data/lib/evaluation_helpers.rb +35 -3
- data/lib/evaluator.rb +332 -327
- data/lib/feature_gate.rb +0 -24
- data/lib/id_list.rb +1 -1
- data/lib/interfaces/data_store.rb +1 -1
- data/lib/interfaces/user_persistent_storage.rb +1 -1
- data/lib/layer.rb +1 -20
- data/lib/network.rb +6 -33
- data/lib/spec_store.rb +86 -74
- data/lib/statsig.rb +72 -97
- data/lib/statsig_driver.rb +63 -70
- data/lib/statsig_errors.rb +1 -1
- data/lib/statsig_event.rb +8 -8
- data/lib/statsig_logger.rb +21 -21
- data/lib/statsig_options.rb +0 -50
- data/lib/statsig_user.rb +27 -68
- data/lib/ua_parser.rb +1 -1
- data/lib/uri_helper.rb +1 -9
- data/lib/user_persistent_storage_utils.rb +0 -17
- metadata +8 -6
data/lib/feature_gate.rb
CHANGED
@@ -1,42 +1,19 @@
|
|
1
|
-
# typed: false
|
2
|
-
|
3
|
-
require 'sorbet-runtime'
|
4
|
-
|
5
1
|
class FeatureGate
|
6
|
-
extend T::Sig
|
7
2
|
|
8
|
-
sig { returns(String) }
|
9
3
|
attr_accessor :name
|
10
4
|
|
11
|
-
sig { returns(T::Boolean) }
|
12
5
|
attr_accessor :value
|
13
6
|
|
14
|
-
sig { returns(String) }
|
15
7
|
attr_accessor :rule_id
|
16
8
|
|
17
|
-
sig { returns(T.nilable(String)) }
|
18
9
|
attr_accessor :group_name
|
19
10
|
|
20
|
-
sig { returns(String) }
|
21
11
|
attr_accessor :id_type
|
22
12
|
|
23
|
-
sig { returns(T.nilable(Statsig::EvaluationDetails)) }
|
24
13
|
attr_accessor :evaluation_details
|
25
14
|
|
26
|
-
sig { returns(T.nilable(T::Array[String])) }
|
27
15
|
attr_accessor :target_app_ids
|
28
16
|
|
29
|
-
sig do
|
30
|
-
params(
|
31
|
-
name: String,
|
32
|
-
value: T::Boolean,
|
33
|
-
rule_id: String,
|
34
|
-
group_name: T.nilable(String),
|
35
|
-
id_type: String,
|
36
|
-
evaluation_details: T.nilable(Statsig::EvaluationDetails),
|
37
|
-
target_app_ids: T.nilable(T::Array[String])
|
38
|
-
).void
|
39
|
-
end
|
40
17
|
def initialize(
|
41
18
|
name,
|
42
19
|
value: false,
|
@@ -55,7 +32,6 @@ class FeatureGate
|
|
55
32
|
@target_app_ids = target_app_ids
|
56
33
|
end
|
57
34
|
|
58
|
-
sig { params(res: Statsig::ConfigResult).returns(FeatureGate) }
|
59
35
|
def self.from_config_result(res)
|
60
36
|
new(
|
61
37
|
res.name,
|
data/lib/id_list.rb
CHANGED
data/lib/layer.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
# typed: false
|
2
|
-
|
3
|
-
require 'sorbet-runtime'
|
4
1
|
##
|
5
2
|
# Contains the current values from Statsig.
|
6
3
|
# Will contain layer default values for all shared parameters in that layer.
|
@@ -9,37 +6,22 @@ require 'sorbet-runtime'
|
|
9
6
|
#
|
10
7
|
# Layers Documentation: https://docs.statsig.com/layers
|
11
8
|
class Layer
|
12
|
-
extend T::Sig
|
13
9
|
|
14
|
-
sig { returns(String) }
|
15
10
|
attr_accessor :name
|
16
11
|
|
17
|
-
sig { returns(String) }
|
18
12
|
attr_accessor :rule_id
|
19
13
|
|
20
|
-
sig { returns(String) }
|
21
14
|
attr_accessor :group_name
|
22
15
|
|
23
|
-
sig do
|
24
|
-
params(
|
25
|
-
name: String,
|
26
|
-
value: T::Hash[String, T.untyped],
|
27
|
-
rule_id: String,
|
28
|
-
group_name: T.nilable(String),
|
29
|
-
allocated_experiment: T.nilable(String),
|
30
|
-
exposure_log_func: T.any(Method, Proc, NilClass)
|
31
|
-
).void
|
32
|
-
end
|
33
16
|
def initialize(name, value = {}, rule_id = '', group_name = nil, allocated_experiment = nil, exposure_log_func = nil)
|
34
17
|
@name = name
|
35
|
-
@value = value
|
18
|
+
@value = value || {}
|
36
19
|
@rule_id = rule_id
|
37
20
|
@group_name = group_name
|
38
21
|
@allocated_experiment = allocated_experiment
|
39
22
|
@exposure_log_func = exposure_log_func
|
40
23
|
end
|
41
24
|
|
42
|
-
sig { params(index: String, default_value: T.untyped).returns(T.untyped) }
|
43
25
|
##
|
44
26
|
# Get the value for the given key (index), falling back to the default_value if it cannot be found.
|
45
27
|
#
|
@@ -55,7 +37,6 @@ class Layer
|
|
55
37
|
@value[index]
|
56
38
|
end
|
57
39
|
|
58
|
-
sig { params(index: String, default_value: T.untyped).returns(T.untyped) }
|
59
40
|
##
|
60
41
|
# Get the value for the given key (index), falling back to the default_value if it cannot be found
|
61
42
|
# or is found to have a different type from the default_value.
|
data/lib/network.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
# typed: true
|
2
|
-
|
3
1
|
require 'http'
|
4
2
|
require 'json'
|
5
3
|
require 'securerandom'
|
6
|
-
|
4
|
+
|
7
5
|
require 'uri_helper'
|
8
6
|
require 'connection_pool'
|
9
7
|
|
@@ -20,9 +18,7 @@ module Statsig
|
|
20
18
|
end
|
21
19
|
|
22
20
|
class Network
|
23
|
-
extend T::Sig
|
24
21
|
|
25
|
-
sig { params(server_secret: String, options: StatsigOptions, backoff_mult: Integer).void }
|
26
22
|
def initialize(server_secret, options, backoff_mult = 10)
|
27
23
|
super()
|
28
24
|
URIHelper.initialize(options)
|
@@ -54,41 +50,18 @@ module Statsig
|
|
54
50
|
end
|
55
51
|
end
|
56
52
|
|
57
|
-
sig do
|
58
|
-
params(since_time: Integer)
|
59
|
-
.returns([T.any(HTTP::Response, NilClass), T.any(StandardError, NilClass)])
|
60
|
-
end
|
61
53
|
def download_config_specs(since_time)
|
62
54
|
get("download_config_specs/#{@server_secret}.json?sinceTime=#{since_time}")
|
63
55
|
end
|
64
56
|
|
65
|
-
class HttpMethod < T::Enum
|
66
|
-
enums do
|
67
|
-
GET = new
|
68
|
-
POST = new
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
sig do
|
73
|
-
params(endpoint: String, retries: Integer, backoff: Integer)
|
74
|
-
.returns([T.any(HTTP::Response, NilClass), T.any(StandardError, NilClass)])
|
75
|
-
end
|
76
57
|
def get(endpoint, retries = 0, backoff = 1)
|
77
|
-
request(
|
58
|
+
request(:GET, endpoint, nil, retries, backoff)
|
78
59
|
end
|
79
60
|
|
80
|
-
sig do
|
81
|
-
params(endpoint: String, body: String, retries: Integer, backoff: Integer)
|
82
|
-
.returns([T.any(HTTP::Response, NilClass), T.any(StandardError, NilClass)])
|
83
|
-
end
|
84
61
|
def post(endpoint, body, retries = 0, backoff = 1)
|
85
|
-
request(
|
62
|
+
request(:POST, endpoint, body, retries, backoff)
|
86
63
|
end
|
87
64
|
|
88
|
-
sig do
|
89
|
-
params(method: HttpMethod, endpoint: String, body: T.nilable(String), retries: Integer, backoff: Integer)
|
90
|
-
.returns([T.any(HTTP::Response, NilClass), T.any(StandardError, NilClass)])
|
91
|
-
end
|
92
65
|
def request(method, endpoint, body, retries = 0, backoff = 1)
|
93
66
|
if @local_mode
|
94
67
|
return nil, nil
|
@@ -107,9 +80,9 @@ module Statsig
|
|
107
80
|
res = @connection_pool.with do |conn|
|
108
81
|
request = conn.headers('STATSIG-CLIENT-TIME' => (Time.now.to_f * 1000).to_i.to_s)
|
109
82
|
case method
|
110
|
-
when
|
83
|
+
when :GET
|
111
84
|
request.get(url)
|
112
|
-
when
|
85
|
+
when :POST
|
113
86
|
request.post(url, body: body)
|
114
87
|
end
|
115
88
|
end
|
@@ -133,7 +106,7 @@ module Statsig
|
|
133
106
|
end
|
134
107
|
|
135
108
|
def post_logs(events)
|
136
|
-
json_body = JSON.generate({
|
109
|
+
json_body = JSON.generate({ events: events, statsigMetadata: Statsig.get_statsig_metadata })
|
137
110
|
post('log_event', json_body, @post_logs_retry_limit)
|
138
111
|
rescue StandardError
|
139
112
|
|
data/lib/spec_store.rb
CHANGED
@@ -1,17 +1,24 @@
|
|
1
|
-
# typed: false
|
2
1
|
require 'net/http'
|
3
2
|
require 'uri'
|
4
3
|
require 'evaluation_details'
|
5
4
|
require 'id_list'
|
6
5
|
require 'concurrent-ruby'
|
7
6
|
require 'hash_utils'
|
7
|
+
require 'api_config'
|
8
8
|
|
9
9
|
module Statsig
|
10
10
|
class SpecStore
|
11
|
-
|
12
11
|
attr_accessor :last_config_sync_time
|
13
12
|
attr_accessor :initial_config_sync_time
|
14
13
|
attr_accessor :init_reason
|
14
|
+
attr_accessor :gates
|
15
|
+
attr_accessor :configs
|
16
|
+
attr_accessor :layers
|
17
|
+
attr_accessor :id_lists
|
18
|
+
attr_accessor :experiment_to_layer
|
19
|
+
attr_accessor :sdk_keys_to_app_ids
|
20
|
+
attr_accessor :hashed_sdk_keys_to_app_ids
|
21
|
+
attr_accessor :unsupported_configs
|
15
22
|
|
16
23
|
def initialize(network, options, error_callback, diagnostics, error_boundary, logger, secret_key)
|
17
24
|
@init_reason = EvaluationReason::UNINITIALIZED
|
@@ -23,25 +30,24 @@ module Statsig
|
|
23
30
|
@rulesets_sync_interval = options.rulesets_sync_interval
|
24
31
|
@id_lists_sync_interval = options.idlists_sync_interval
|
25
32
|
@rules_updated_callback = options.rules_updated_callback
|
26
|
-
@
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
:hashed_sdk_keys_to_app_ids => {}
|
34
|
-
}
|
33
|
+
@gates = {}
|
34
|
+
@configs = {}
|
35
|
+
@layers = {}
|
36
|
+
@id_lists = {}
|
37
|
+
@experiment_to_layer = {}
|
38
|
+
@sdk_keys_to_app_ids = {}
|
39
|
+
@hashed_sdk_keys_to_app_ids = {}
|
35
40
|
@diagnostics = diagnostics
|
36
41
|
@error_boundary = error_boundary
|
37
42
|
@logger = logger
|
38
43
|
@secret_key = secret_key
|
44
|
+
@unsupported_configs = Set.new
|
39
45
|
|
40
46
|
@id_list_thread_pool = Concurrent::FixedThreadPool.new(
|
41
47
|
options.idlist_threadpool_size,
|
42
48
|
name: 'statsig-idlist',
|
43
49
|
max_queue: 100,
|
44
|
-
fallback_policy: :discard
|
50
|
+
fallback_policy: :discard
|
45
51
|
)
|
46
52
|
|
47
53
|
unless @options.bootstrap_values.nil?
|
@@ -53,7 +59,7 @@ module Statsig
|
|
53
59
|
if process_specs(options.bootstrap_values)
|
54
60
|
@init_reason = EvaluationReason::BOOTSTRAP
|
55
61
|
end
|
56
|
-
rescue
|
62
|
+
rescue StandardError
|
57
63
|
puts 'the provided bootstrapValues is not a valid JSON string'
|
58
64
|
ensure
|
59
65
|
tracker.end(success: @init_reason == EvaluationReason::BOOTSTRAP)
|
@@ -96,70 +102,61 @@ module Statsig
|
|
96
102
|
end
|
97
103
|
|
98
104
|
def has_gate?(gate_name)
|
99
|
-
@
|
105
|
+
@gates.key?(gate_name)
|
100
106
|
end
|
101
107
|
|
102
108
|
def has_config?(config_name)
|
103
|
-
@
|
109
|
+
@configs.key?(config_name)
|
104
110
|
end
|
105
111
|
|
106
112
|
def has_layer?(layer_name)
|
107
|
-
@
|
113
|
+
@layers.key?(layer_name)
|
108
114
|
end
|
109
115
|
|
110
116
|
def get_gate(gate_name)
|
111
117
|
return nil unless has_gate?(gate_name)
|
112
|
-
|
118
|
+
|
119
|
+
@gates[gate_name]
|
113
120
|
end
|
114
121
|
|
115
122
|
def get_config(config_name)
|
116
123
|
return nil unless has_config?(config_name)
|
117
|
-
|
124
|
+
|
125
|
+
@configs[config_name]
|
118
126
|
end
|
119
127
|
|
120
128
|
def get_layer(layer_name)
|
121
129
|
return nil unless has_layer?(layer_name)
|
122
|
-
@specs[:layers][layer_name]
|
123
|
-
end
|
124
130
|
|
125
|
-
|
126
|
-
@specs[:gates]
|
127
|
-
end
|
128
|
-
|
129
|
-
def configs
|
130
|
-
@specs[:configs]
|
131
|
-
end
|
132
|
-
|
133
|
-
def layers
|
134
|
-
@specs[:layers]
|
131
|
+
@layers[layer_name]
|
135
132
|
end
|
136
133
|
|
137
134
|
def get_id_list(list_name)
|
138
|
-
@
|
135
|
+
@id_lists[list_name]
|
139
136
|
end
|
140
137
|
|
141
138
|
def has_sdk_key?(sdk_key)
|
142
|
-
@
|
139
|
+
@sdk_keys_to_app_ids.key?(sdk_key)
|
143
140
|
end
|
144
141
|
|
145
142
|
def has_hashed_sdk_key?(hashed_sdk_key)
|
146
|
-
@
|
143
|
+
@hashed_sdk_keys_to_app_ids.key?(hashed_sdk_key)
|
147
144
|
end
|
148
145
|
|
149
146
|
def get_app_id_for_sdk_key(sdk_key)
|
150
147
|
if sdk_key.nil?
|
151
148
|
return nil
|
152
149
|
end
|
153
|
-
|
150
|
+
|
151
|
+
hashed_sdk_key = Statsig::HashUtils.djb2(sdk_key).to_sym
|
154
152
|
if has_hashed_sdk_key?(hashed_sdk_key)
|
155
|
-
return @
|
153
|
+
return @hashed_sdk_keys_to_app_ids[hashed_sdk_key]
|
156
154
|
end
|
157
|
-
return nil unless has_sdk_key?(sdk_key)
|
158
|
-
@specs[:sdk_keys_to_app_ids][sdk_key]
|
159
|
-
end
|
160
155
|
|
161
|
-
|
162
|
-
|
156
|
+
key = sdk_key.to_sym
|
157
|
+
return nil unless has_sdk_key?(key)
|
158
|
+
|
159
|
+
@sdk_keys_to_app_ids[key]
|
163
160
|
end
|
164
161
|
|
165
162
|
def maybe_restart_background_threads
|
@@ -211,6 +208,7 @@ module Statsig
|
|
211
208
|
if @options.data_store.nil?
|
212
209
|
return
|
213
210
|
end
|
211
|
+
|
214
212
|
@options.data_store.set(Interfaces::IDataStore::CONFIG_SPECS_KEY, specs_string)
|
215
213
|
end
|
216
214
|
|
@@ -264,7 +262,10 @@ module Statsig
|
|
264
262
|
end
|
265
263
|
tracker.end(success: @init_reason == EvaluationReason::NETWORK)
|
266
264
|
|
267
|
-
|
265
|
+
unless response.body.nil? or @rules_updated_callback.nil?
|
266
|
+
@rules_updated_callback.call(response.body.to_s,
|
267
|
+
@last_config_sync_time)
|
268
|
+
end
|
268
269
|
end
|
269
270
|
|
270
271
|
nil
|
@@ -283,43 +284,41 @@ module Statsig
|
|
283
284
|
return false
|
284
285
|
end
|
285
286
|
|
286
|
-
specs_json = JSON.parse(specs_string)
|
287
|
+
specs_json = JSON.parse(specs_string, { symbolize_names: true })
|
287
288
|
return false unless specs_json.is_a? Hash
|
288
289
|
|
289
|
-
hashed_sdk_key_used = specs_json[
|
290
|
+
hashed_sdk_key_used = specs_json[:hashed_sdk_key_used]
|
290
291
|
unless hashed_sdk_key_used.nil? or hashed_sdk_key_used == Statsig::HashUtils.djb2(@secret_key)
|
291
292
|
err_boundary.log_exception(Statsig::InvalidSDKKeyResponse.new)
|
292
293
|
return false
|
293
294
|
end
|
294
295
|
|
295
|
-
@last_config_sync_time = specs_json[
|
296
|
-
return false unless specs_json[
|
297
|
-
|
298
|
-
|
299
|
-
|
296
|
+
@last_config_sync_time = specs_json[:time] || @last_config_sync_time
|
297
|
+
return false unless specs_json[:has_updates] == true &&
|
298
|
+
!specs_json[:feature_gates].nil? &&
|
299
|
+
!specs_json[:dynamic_configs].nil? &&
|
300
|
+
!specs_json[:layer_configs].nil?
|
300
301
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
302
|
+
@unsupported_configs.clear()
|
303
|
+
new_gates = process_configs(specs_json[:feature_gates])
|
304
|
+
new_configs = process_configs(specs_json[:dynamic_configs])
|
305
|
+
new_layers = process_configs(specs_json[:layer_configs])
|
305
306
|
|
306
|
-
|
307
|
-
specs_json[
|
308
|
-
specs_json['layer_configs'].each { |layer| new_layers[layer['name']] = layer }
|
309
|
-
specs_json['diagnostics']&.each { |key, value| @diagnostics.sample_rates[key] = value }
|
307
|
+
new_exp_to_layer = {}
|
308
|
+
specs_json[:diagnostics]&.each { |key, value| @diagnostics.sample_rates[key.to_s] = value }
|
310
309
|
|
311
|
-
if specs_json[
|
312
|
-
specs_json[
|
310
|
+
if specs_json[:layers].is_a?(Hash)
|
311
|
+
specs_json[:layers].each do |layer_name, experiments|
|
313
312
|
experiments.each { |experiment_name| new_exp_to_layer[experiment_name] = layer_name }
|
314
|
-
|
313
|
+
end
|
315
314
|
end
|
316
315
|
|
317
|
-
@
|
318
|
-
@
|
319
|
-
@
|
320
|
-
@
|
321
|
-
@
|
322
|
-
@
|
316
|
+
@gates = new_gates
|
317
|
+
@configs = new_configs
|
318
|
+
@layers = new_layers
|
319
|
+
@experiment_to_layer = new_exp_to_layer
|
320
|
+
@sdk_keys_to_app_ids = specs_json[:sdk_keys_to_app_ids] || {}
|
321
|
+
@hashed_sdk_keys_to_app_ids = specs_json[:hashed_sdk_keys_to_app_ids] || {}
|
323
322
|
|
324
323
|
unless from_adapter
|
325
324
|
save_config_specs_to_storage_adapter(specs_string)
|
@@ -327,6 +326,17 @@ module Statsig
|
|
327
326
|
true
|
328
327
|
end
|
329
328
|
|
329
|
+
def process_configs(configs)
|
330
|
+
configs.each_with_object({}) do |config, new_configs|
|
331
|
+
begin
|
332
|
+
new_configs[config[:name]] = APIConfig.from_json(config)
|
333
|
+
rescue UnsupportedConfigException => e
|
334
|
+
@unsupported_configs.add(config[:name])
|
335
|
+
nil
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
330
340
|
def get_id_lists_from_adapter(context)
|
331
341
|
tracker = @diagnostics.track(context, 'data_store_id_lists', 'fetch')
|
332
342
|
cached_values = @options.data_store.get(Interfaces::IDataStore::ID_LISTS_KEY)
|
@@ -345,6 +355,7 @@ module Statsig
|
|
345
355
|
if @options.data_store.nil?
|
346
356
|
return
|
347
357
|
end
|
358
|
+
|
348
359
|
@options.data_store.set(Interfaces::IDataStore::ID_LISTS_KEY, id_lists_raw_json)
|
349
360
|
end
|
350
361
|
|
@@ -357,7 +368,7 @@ module Statsig
|
|
357
368
|
end
|
358
369
|
success = e.nil? && !response.nil?
|
359
370
|
tracker.end(statusCode: code, success: success, sdkRegion: response&.headers&.[]('X-Statsig-Region'))
|
360
|
-
|
371
|
+
unless success
|
361
372
|
return
|
362
373
|
end
|
363
374
|
|
@@ -365,16 +376,17 @@ module Statsig
|
|
365
376
|
server_id_lists = JSON.parse(response)
|
366
377
|
process_id_lists(server_id_lists, context)
|
367
378
|
save_id_lists_to_adapter(response.body.to_s)
|
368
|
-
rescue
|
379
|
+
rescue StandardError
|
369
380
|
# Ignored, will try again
|
370
381
|
end
|
371
382
|
end
|
372
383
|
|
373
384
|
def process_id_lists(new_id_lists, context, from_adapter: false)
|
374
|
-
local_id_lists = @
|
385
|
+
local_id_lists = @id_lists
|
375
386
|
if !new_id_lists.is_a?(Hash) || !local_id_lists.is_a?(Hash)
|
376
387
|
return
|
377
388
|
end
|
389
|
+
|
378
390
|
tasks = []
|
379
391
|
|
380
392
|
tracker = @diagnostics.track(context,
|
@@ -389,7 +401,7 @@ module Statsig
|
|
389
401
|
end
|
390
402
|
|
391
403
|
delete_lists = []
|
392
|
-
local_id_lists.each do |list_name,
|
404
|
+
local_id_lists.each do |list_name, _list|
|
393
405
|
unless new_id_lists.key? list_name
|
394
406
|
delete_lists.push list_name
|
395
407
|
end
|
@@ -425,7 +437,7 @@ module Statsig
|
|
425
437
|
next
|
426
438
|
end
|
427
439
|
|
428
|
-
tasks << Concurrent::Promise.execute(:
|
440
|
+
tasks << Concurrent::Promise.execute(executor: @id_list_thread_pool) do
|
429
441
|
if from_adapter
|
430
442
|
get_single_id_list_from_adapter(local_list, context)
|
431
443
|
else
|
@@ -468,7 +480,7 @@ module Statsig
|
|
468
480
|
content = res.body.to_s
|
469
481
|
success = process_single_id_list(list, context, content, content_length)
|
470
482
|
save_single_id_list_to_adapter(list.name, content) unless success.nil? || !success
|
471
|
-
rescue
|
483
|
+
rescue StandardError
|
472
484
|
tracker.end(success: false)
|
473
485
|
nil
|
474
486
|
end
|
@@ -479,7 +491,7 @@ module Statsig
|
|
479
491
|
begin
|
480
492
|
tracker = @diagnostics.track(context, from_adapter ? 'data_store_id_list' : 'get_id_list', 'process', { url: list.url })
|
481
493
|
unless content.is_a?(String) && (content[0] == '-' || content[0] == '+')
|
482
|
-
@
|
494
|
+
@id_lists.delete(list.name)
|
483
495
|
tracker.end(success: false)
|
484
496
|
return false
|
485
497
|
end
|
@@ -488,6 +500,7 @@ module Statsig
|
|
488
500
|
lines.each do |li|
|
489
501
|
line = li.strip
|
490
502
|
next if line.length <= 1
|
503
|
+
|
491
504
|
op = line[0]
|
492
505
|
id = line[1..line.length]
|
493
506
|
if op == '+'
|
@@ -504,11 +517,10 @@ module Statsig
|
|
504
517
|
end
|
505
518
|
tracker.end(success: true)
|
506
519
|
return true
|
507
|
-
rescue
|
520
|
+
rescue StandardError
|
508
521
|
tracker.end(success: false)
|
509
522
|
return false
|
510
523
|
end
|
511
524
|
end
|
512
|
-
|
513
525
|
end
|
514
526
|
end
|