statsig 1.22.0 → 1.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/interfaces/data_store.rb +1 -0
- data/lib/spec_store.rb +139 -66
- data/lib/statsig.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89aaef291fd8cbd82af8bcf380894ea554588988b2de66507482420b475c8367
|
4
|
+
data.tar.gz: 3f535a1ce2a9af6e811c77df334b3b58a3c2503857f74bbf5b280764cfb041f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16074e02fd22c2d0bc64e8adbd4b2551d46509e60f161b9ff5009ea28e13a995f13e0abd89c48c1c737d80dad1a869b8f15de8ac2b896a47ad6da54cdadbdd70
|
7
|
+
data.tar.gz: ac6e8241c8132844a0ee5e8858578ab3475cb227e48847fb840d4ef15219828f58c38f24d2c2e5e2bfe26714bda07f8c1068f9a15af675d4320e4a90c6c2a7c6
|
data/lib/spec_store.rb
CHANGED
@@ -42,7 +42,7 @@ module Statsig
|
|
42
42
|
puts 'data_store gets priority over bootstrap_values. bootstrap_values will be ignored'
|
43
43
|
else
|
44
44
|
init_diagnostics&.mark("bootstrap", "start", "load")
|
45
|
-
if
|
45
|
+
if process_specs(options.bootstrap_values)
|
46
46
|
@init_reason = EvaluationReason::BOOTSTRAP
|
47
47
|
end
|
48
48
|
init_diagnostics&.mark("bootstrap", "end", "load", @init_reason == EvaluationReason::BOOTSTRAP)
|
@@ -55,7 +55,7 @@ module Statsig
|
|
55
55
|
unless @options.data_store.nil?
|
56
56
|
init_diagnostics&.mark("data_store", "start", "load")
|
57
57
|
@options.data_store.init
|
58
|
-
|
58
|
+
load_config_specs_from_storage_adapter(init_diagnostics: init_diagnostics)
|
59
59
|
init_diagnostics&.mark("data_store", "end", "load", @init_reason == EvaluationReason::DATA_ADAPTER)
|
60
60
|
end
|
61
61
|
|
@@ -64,7 +64,11 @@ module Statsig
|
|
64
64
|
end
|
65
65
|
|
66
66
|
@initial_config_sync_time = @last_config_sync_time == 0 ? -1 : @last_config_sync_time
|
67
|
-
|
67
|
+
if !@options.data_store.nil?
|
68
|
+
get_id_lists_from_adapter(init_diagnostics)
|
69
|
+
else
|
70
|
+
get_id_lists_from_network(init_diagnostics)
|
71
|
+
end
|
68
72
|
|
69
73
|
@config_sync_thread = sync_config_specs
|
70
74
|
@id_lists_sync_thread = sync_id_lists
|
@@ -130,16 +134,23 @@ module Statsig
|
|
130
134
|
|
131
135
|
private
|
132
136
|
|
133
|
-
def
|
137
|
+
def load_config_specs_from_storage_adapter(init_diagnostics: nil)
|
138
|
+
init_diagnostics&.mark("download_config_specs", "start", "fetch_from_adapter")
|
134
139
|
cached_values = @options.data_store.get(Interfaces::IDataStore::CONFIG_SPECS_KEY)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
140
|
+
init_diagnostics&.mark("download_config_specs", "end", "fetch_from_adapter", true)
|
141
|
+
return if cached_values.nil?
|
142
|
+
|
143
|
+
init_diagnostics&.mark("download_config_specs", "start", "process")
|
144
|
+
process_specs(cached_values, from_adapter: true)
|
139
145
|
@init_reason = EvaluationReason::DATA_ADAPTER
|
146
|
+
init_diagnostics&.mark("download_config_specs", "end", "process", @init_reason)
|
147
|
+
rescue StandardError
|
148
|
+
# Fallback to network
|
149
|
+
init_diagnostics&.mark("download_config_specs", "end", "fetch_from_adapter", false)
|
150
|
+
download_config_specs(init_diagnostics)
|
140
151
|
end
|
141
152
|
|
142
|
-
def
|
153
|
+
def save_config_specs_to_storage_adapter(specs_string)
|
143
154
|
if @options.data_store.nil?
|
144
155
|
return
|
145
156
|
end
|
@@ -151,7 +162,7 @@ module Statsig
|
|
151
162
|
loop do
|
152
163
|
sleep @options.rulesets_sync_interval
|
153
164
|
if @options.data_store&.should_be_used_for_querying_updates(Interfaces::IDataStore::CONFIG_SPECS_KEY)
|
154
|
-
|
165
|
+
load_config_specs_from_storage_adapter
|
155
166
|
else
|
156
167
|
download_config_specs
|
157
168
|
end
|
@@ -163,7 +174,11 @@ module Statsig
|
|
163
174
|
Thread.new do
|
164
175
|
loop do
|
165
176
|
sleep @id_lists_sync_interval
|
166
|
-
|
177
|
+
if @options.data_store&.should_be_used_for_querying_updates(Interfaces::IDataStore::ID_LISTS_KEY)
|
178
|
+
get_id_lists_from_adapter
|
179
|
+
else
|
180
|
+
get_id_lists_from_network
|
181
|
+
end
|
167
182
|
end
|
168
183
|
end
|
169
184
|
end
|
@@ -184,7 +199,7 @@ module Statsig
|
|
184
199
|
unless response.nil?
|
185
200
|
init_diagnostics&.mark("download_config_specs", "start", "process")
|
186
201
|
|
187
|
-
if
|
202
|
+
if process_specs(response.body)
|
188
203
|
@init_reason = EvaluationReason::NETWORK
|
189
204
|
@rules_updated_callback.call(response.body.to_s, @last_config_sync_time) unless response.body.nil? or @rules_updated_callback.nil?
|
190
205
|
end
|
@@ -203,7 +218,7 @@ module Statsig
|
|
203
218
|
@error_callback.call(error) unless error.nil? or @error_callback.nil?
|
204
219
|
end
|
205
220
|
|
206
|
-
def
|
221
|
+
def process_specs(specs_string, from_adapter: false)
|
207
222
|
if specs_string.nil?
|
208
223
|
return false
|
209
224
|
end
|
@@ -238,12 +253,33 @@ module Statsig
|
|
238
253
|
@specs[:experiment_to_layer] = new_exp_to_layer
|
239
254
|
|
240
255
|
unless from_adapter
|
241
|
-
|
256
|
+
save_config_specs_to_storage_adapter(specs_string)
|
242
257
|
end
|
243
258
|
true
|
244
259
|
end
|
245
260
|
|
246
|
-
def
|
261
|
+
def get_id_lists_from_adapter(init_diagnostics = nil)
|
262
|
+
init_diagnostics&.mark("get_id_lists", "start", "fetch_from_adapter")
|
263
|
+
cached_values = @options.data_store.get(Interfaces::IDataStore::ID_LISTS_KEY)
|
264
|
+
return if cached_values.nil?
|
265
|
+
|
266
|
+
init_diagnostics&.mark("get_id_lists", "end", "fetch_from_adapter", true)
|
267
|
+
id_lists = JSON.parse(cached_values)
|
268
|
+
process_id_lists(id_lists, init_diagnostics, from_adapter: true)
|
269
|
+
rescue StandardError
|
270
|
+
# Fallback to network
|
271
|
+
init_diagnostics&.mark("get_id_lists", "end", "fetch_from_adapter", false)
|
272
|
+
get_id_lists_from_network(init_diagnostics)
|
273
|
+
end
|
274
|
+
|
275
|
+
def save_id_lists_to_adapter(id_lists)
|
276
|
+
if @options.data_store.nil?
|
277
|
+
return
|
278
|
+
end
|
279
|
+
@options.data_store.set(Interfaces::IDataStore::CONFIG_SPECS_KEY, JSON.generate(id_lists))
|
280
|
+
end
|
281
|
+
|
282
|
+
def get_id_lists_from_network(init_diagnostics = nil)
|
247
283
|
init_diagnostics&.mark("get_id_lists", "start", "network_request")
|
248
284
|
response, e = @network.post_helper('get_id_lists', JSON.generate({ 'statsigMetadata' => Statsig.get_statsig_metadata }))
|
249
285
|
if !e.nil? || response.nil?
|
@@ -253,69 +289,91 @@ module Statsig
|
|
253
289
|
|
254
290
|
begin
|
255
291
|
server_id_lists = JSON.parse(response)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
if server_id_lists.length == 0
|
263
|
-
return
|
264
|
-
end
|
292
|
+
process_id_lists(server_id_lists, init_diagnostics)
|
293
|
+
rescue
|
294
|
+
# Ignored, will try again
|
295
|
+
end
|
296
|
+
end
|
265
297
|
|
266
|
-
|
298
|
+
def process_id_lists(new_id_lists, init_diagnostics, from_adapter: false)
|
299
|
+
local_id_lists = @specs[:id_lists]
|
300
|
+
if !new_id_lists.is_a?(Hash) || !local_id_lists.is_a?(Hash)
|
301
|
+
return
|
302
|
+
end
|
303
|
+
tasks = []
|
267
304
|
|
268
|
-
|
269
|
-
|
270
|
-
|
305
|
+
if new_id_lists.length == 0
|
306
|
+
return
|
307
|
+
end
|
271
308
|
|
272
|
-
|
273
|
-
local_list = IDList.new(list)
|
274
|
-
local_list.size = 0
|
275
|
-
local_id_lists[list_name] = local_list
|
276
|
-
end
|
309
|
+
init_diagnostics&.mark("get_id_lists", "start", "process", new_id_lists.length)
|
277
310
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
end
|
311
|
+
new_id_lists.each do |list_name, list|
|
312
|
+
new_list = IDList.new(list)
|
313
|
+
local_list = get_id_list(list_name)
|
282
314
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
end
|
315
|
+
unless local_list.is_a? IDList
|
316
|
+
local_list = IDList.new(list)
|
317
|
+
local_list.size = 0
|
318
|
+
local_id_lists[list_name] = local_list
|
319
|
+
end
|
289
320
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
321
|
+
# skip if server list is invalid
|
322
|
+
if new_list.url.nil? || new_list.creation_time < local_list.creation_time || new_list.file_id.nil?
|
323
|
+
next
|
324
|
+
end
|
294
325
|
|
295
|
-
|
296
|
-
|
297
|
-
|
326
|
+
# reset local list if server list returns a newer file
|
327
|
+
if new_list.file_id != local_list.file_id && new_list.creation_time >= local_list.creation_time
|
328
|
+
local_list = IDList.new(list)
|
329
|
+
local_list.size = 0
|
330
|
+
local_id_lists[list_name] = local_list
|
298
331
|
end
|
299
332
|
|
300
|
-
|
301
|
-
if
|
302
|
-
|
303
|
-
return # timed out
|
333
|
+
# skip if server list is no bigger than local list, which means nothing new to read
|
334
|
+
if new_list.size <= local_list.size
|
335
|
+
next
|
304
336
|
end
|
305
337
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
338
|
+
tasks << Concurrent::Promise.execute(:executor => @id_list_thread_pool) do
|
339
|
+
if from_adapter
|
340
|
+
get_single_id_list_from_adapter(local_list)
|
341
|
+
else
|
342
|
+
download_single_id_list(local_list)
|
310
343
|
end
|
311
344
|
end
|
312
|
-
|
313
|
-
|
345
|
+
end
|
346
|
+
|
347
|
+
result = Concurrent::Promise.all?(*tasks).execute.wait(@id_lists_sync_interval)
|
348
|
+
if result.state != :fulfilled
|
349
|
+
init_diagnostics&.mark("get_id_lists", "end", "process", false)
|
350
|
+
return # timed out
|
351
|
+
end
|
352
|
+
|
353
|
+
delete_lists = []
|
354
|
+
local_id_lists.each do |list_name, list|
|
355
|
+
unless new_id_lists.key? list_name
|
356
|
+
delete_lists.push list_name
|
314
357
|
end
|
315
|
-
init_diagnostics&.mark("get_id_lists", "end", "process", true)
|
316
|
-
rescue
|
317
|
-
# Ignored, will try again
|
318
358
|
end
|
359
|
+
delete_lists.each do |list_name|
|
360
|
+
local_id_lists.delete list_name
|
361
|
+
end
|
362
|
+
init_diagnostics&.mark("get_id_lists", "end", "process", true)
|
363
|
+
end
|
364
|
+
|
365
|
+
def get_single_id_list_from_adapter(list)
|
366
|
+
cached_values = @options.data_store.get("#{Interfaces::IDataStore::ID_LISTS_KEY}::#{list.name}")
|
367
|
+
content = cached_values.to_s
|
368
|
+
process_single_id_list(list, content)
|
369
|
+
rescue StandardError
|
370
|
+
nil
|
371
|
+
end
|
372
|
+
|
373
|
+
def save_single_id_list_to_adapter(name, content)
|
374
|
+
return if @options.data_store.nil?
|
375
|
+
|
376
|
+
@options.data_store.set("#{Interfaces::IDataStore::ID_LISTS_KEY}::#{name}", content)
|
319
377
|
end
|
320
378
|
|
321
379
|
def download_single_id_list(list)
|
@@ -327,9 +385,19 @@ module Statsig
|
|
327
385
|
content_length = Integer(res['content-length'])
|
328
386
|
nil if content_length.nil? || content_length <= 0
|
329
387
|
content = res.body.to_s
|
388
|
+
success = process_single_id_list(list, content, content_length)
|
389
|
+
save_single_id_list_to_adapter(list.name, content) unless success.nil? || !success
|
390
|
+
rescue
|
391
|
+
nil
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def process_single_id_list(list, content, content_length = nil)
|
396
|
+
false unless list.is_a? IDList
|
397
|
+
begin
|
330
398
|
unless content.is_a?(String) && (content[0] == '-' || content[0] == '+')
|
331
399
|
@specs[:id_lists].delete(list.name)
|
332
|
-
return
|
400
|
+
return false
|
333
401
|
end
|
334
402
|
ids_clone = list.ids # clone the list, operate on the new list, and swap out the old list, so the operation is thread-safe
|
335
403
|
lines = content.split(/\r?\n/)
|
@@ -345,9 +413,14 @@ module Statsig
|
|
345
413
|
end
|
346
414
|
end
|
347
415
|
list.ids = ids_clone
|
348
|
-
list.size =
|
416
|
+
list.size = if content_length.nil?
|
417
|
+
list.size + content.bytesize
|
418
|
+
else
|
419
|
+
list.size + content_length
|
420
|
+
end
|
421
|
+
return true
|
349
422
|
rescue
|
350
|
-
|
423
|
+
return false
|
351
424
|
end
|
352
425
|
end
|
353
426
|
end
|
data/lib/statsig.rb
CHANGED
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.
|
4
|
+
version: 1.23.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: 2023-
|
11
|
+
date: 2023-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|