splitclient-rb 7.1.4.pre.rc7-java → 7.1.4.pre.rc8-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.txt +1 -1
- data/Rakefile +7 -2
- data/ext/murmurhash/MurmurHash3.java +139 -0
- data/lib/splitclient-rb.rb +6 -1
- data/lib/splitclient-rb/cache/hashers/impression_hasher.rb +34 -0
- data/lib/splitclient-rb/cache/observers/impression_observer.rb +22 -0
- data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +4 -18
- data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +7 -18
- data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +1 -27
- data/lib/splitclient-rb/cache/routers/impression_router.rb +12 -14
- data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +73 -0
- data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +11 -11
- data/lib/splitclient-rb/cache/senders/impressions_sender.rb +3 -3
- data/lib/splitclient-rb/clients/split_client.rb +24 -73
- data/lib/splitclient-rb/engine/api/impressions.rb +30 -13
- data/lib/splitclient-rb/engine/common/impressions_counter.rb +45 -0
- data/lib/splitclient-rb/engine/common/impressions_manager.rb +87 -0
- data/lib/splitclient-rb/engine/evaluator/splitter.rb +1 -5
- data/lib/splitclient-rb/engine/parser/evaluator.rb +0 -4
- data/lib/splitclient-rb/engine/sync_manager.rb +5 -6
- data/lib/splitclient-rb/engine/synchronizer.rb +9 -1
- data/lib/splitclient-rb/split_config.rb +31 -1
- data/lib/splitclient-rb/split_factory.rb +5 -2
- data/lib/splitclient-rb/version.rb +1 -1
- data/splitclient-rb.gemspec +8 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 587fa9e98277f3a3f2551240e421d5a734ed821e
|
4
|
+
data.tar.gz: 9ba4dd4290b4b61e18e29c3390a901bdd9e9aebd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 737188f30254868feebdf799ddc9e4a54ef840db885aac1c1155b1e4bc988e112e15ec7879d89bc31911abb334b08379211b7cf93d5735124b330d2f93bf7704
|
7
|
+
data.tar.gz: e96eca60d3dc2c53c2667f0a4eee50ed960865af99d6108788598589f8d16cc5b2d33d61a9ae9616f2446888c9a527b8019eecd459ade92ecce66aedac9c2224
|
data/CHANGES.txt
CHANGED
data/Rakefile
CHANGED
@@ -11,14 +11,19 @@ RSpec::Core::RakeTask.new(:spec)
|
|
11
11
|
RuboCop::RakeTask.new(:rubocop)
|
12
12
|
|
13
13
|
task spec: :compile
|
14
|
-
|
15
|
-
|
14
|
+
case RUBY_PLATFORM
|
15
|
+
when 'java'
|
16
16
|
require 'rake/javaextensiontask'
|
17
17
|
Rake::JavaExtensionTask.new 'murmurhash' do |ext|
|
18
18
|
ext.lib_dir = 'lib/murmurhash'
|
19
19
|
ext.target_version = '1.7'
|
20
20
|
ext.source_version = '1.7'
|
21
21
|
end
|
22
|
+
else
|
23
|
+
require 'rake/extensiontask'
|
24
|
+
Rake::ExtensionTask.new 'murmurhash' do |ext|
|
25
|
+
ext.lib_dir = 'lib/murmurhash'
|
26
|
+
end
|
22
27
|
end
|
23
28
|
|
24
29
|
if !ENV['APPRAISAL_INITIALIZED']
|
@@ -159,4 +159,143 @@ public final class MurmurHash3 {
|
|
159
159
|
|
160
160
|
return h1 & 0xFFFFFFFFL;
|
161
161
|
}
|
162
|
+
|
163
|
+
// The following set of methods and constants are borrowed from:
|
164
|
+
// `This method is borrowed from `org.apache.commons.codec.digest.MurmurHash3`
|
165
|
+
|
166
|
+
// Constants for 128-bit variant
|
167
|
+
private static final long C1 = 0x87c37b91114253d5L;
|
168
|
+
private static final long C2 = 0x4cf5ad432745937fL;
|
169
|
+
private static final int R1 = 31;
|
170
|
+
private static final int R2 = 27;
|
171
|
+
private static final int R3 = 33;
|
172
|
+
private static final int M = 5;
|
173
|
+
private static final int N1 = 0x52dce729;
|
174
|
+
private static final int N2 = 0x38495ab5;
|
175
|
+
|
176
|
+
/**
|
177
|
+
* Gets the little-endian long from 8 bytes starting at the specified index.
|
178
|
+
*
|
179
|
+
* @param data The data
|
180
|
+
* @param index The index
|
181
|
+
* @return The little-endian long
|
182
|
+
*/
|
183
|
+
private static long getLittleEndianLong(final byte[] data, final int index) {
|
184
|
+
return (((long) data[index ] & 0xff) ) |
|
185
|
+
(((long) data[index + 1] & 0xff) << 8) |
|
186
|
+
(((long) data[index + 2] & 0xff) << 16) |
|
187
|
+
(((long) data[index + 3] & 0xff) << 24) |
|
188
|
+
(((long) data[index + 4] & 0xff) << 32) |
|
189
|
+
(((long) data[index + 5] & 0xff) << 40) |
|
190
|
+
(((long) data[index + 6] & 0xff) << 48) |
|
191
|
+
(((long) data[index + 7] & 0xff) << 56);
|
192
|
+
}
|
193
|
+
|
194
|
+
public static long[] hash128x64(final byte[] data) {
|
195
|
+
return hash128x64(data, 0, data.length, 0);
|
196
|
+
}
|
197
|
+
|
198
|
+
/**
|
199
|
+
* Generates 128-bit hash from the byte array with the given offset, length and seed.
|
200
|
+
*
|
201
|
+
* <p>This is an implementation of the 128-bit hash function {@code MurmurHash3_x64_128}
|
202
|
+
* from from Austin Applyby's original MurmurHash3 {@code c++} code in SMHasher.</p>
|
203
|
+
*
|
204
|
+
* @param data The input byte array
|
205
|
+
* @param offset The first element of array
|
206
|
+
* @param length The length of array
|
207
|
+
* @param seed The initial seed value
|
208
|
+
* @return The 128-bit hash (2 longs)
|
209
|
+
*/
|
210
|
+
public static long[] hash128x64(final byte[] data, final int offset, final int length, final long seed) {
|
211
|
+
long h1 = seed;
|
212
|
+
long h2 = seed;
|
213
|
+
final int nblocks = length >> 4;
|
214
|
+
|
215
|
+
// body
|
216
|
+
for (int i = 0; i < nblocks; i++) {
|
217
|
+
final int index = offset + (i << 4);
|
218
|
+
long k1 = getLittleEndianLong(data, index);
|
219
|
+
long k2 = getLittleEndianLong(data, index + 8);
|
220
|
+
|
221
|
+
// mix functions for k1
|
222
|
+
k1 *= C1;
|
223
|
+
k1 = Long.rotateLeft(k1, R1);
|
224
|
+
k1 *= C2;
|
225
|
+
h1 ^= k1;
|
226
|
+
h1 = Long.rotateLeft(h1, R2);
|
227
|
+
h1 += h2;
|
228
|
+
h1 = h1 * M + N1;
|
229
|
+
|
230
|
+
// mix functions for k2
|
231
|
+
k2 *= C2;
|
232
|
+
k2 = Long.rotateLeft(k2, R3);
|
233
|
+
k2 *= C1;
|
234
|
+
h2 ^= k2;
|
235
|
+
h2 = Long.rotateLeft(h2, R1);
|
236
|
+
h2 += h1;
|
237
|
+
h2 = h2 * M + N2;
|
238
|
+
}
|
239
|
+
|
240
|
+
// tail
|
241
|
+
long k1 = 0;
|
242
|
+
long k2 = 0;
|
243
|
+
final int index = offset + (nblocks << 4);
|
244
|
+
switch (offset + length - index) {
|
245
|
+
case 15:
|
246
|
+
k2 ^= ((long) data[index + 14] & 0xff) << 48;
|
247
|
+
case 14:
|
248
|
+
k2 ^= ((long) data[index + 13] & 0xff) << 40;
|
249
|
+
case 13:
|
250
|
+
k2 ^= ((long) data[index + 12] & 0xff) << 32;
|
251
|
+
case 12:
|
252
|
+
k2 ^= ((long) data[index + 11] & 0xff) << 24;
|
253
|
+
case 11:
|
254
|
+
k2 ^= ((long) data[index + 10] & 0xff) << 16;
|
255
|
+
case 10:
|
256
|
+
k2 ^= ((long) data[index + 9] & 0xff) << 8;
|
257
|
+
case 9:
|
258
|
+
k2 ^= data[index + 8] & 0xff;
|
259
|
+
k2 *= C2;
|
260
|
+
k2 = Long.rotateLeft(k2, R3);
|
261
|
+
k2 *= C1;
|
262
|
+
h2 ^= k2;
|
263
|
+
|
264
|
+
case 8:
|
265
|
+
k1 ^= ((long) data[index + 7] & 0xff) << 56;
|
266
|
+
case 7:
|
267
|
+
k1 ^= ((long) data[index + 6] & 0xff) << 48;
|
268
|
+
case 6:
|
269
|
+
k1 ^= ((long) data[index + 5] & 0xff) << 40;
|
270
|
+
case 5:
|
271
|
+
k1 ^= ((long) data[index + 4] & 0xff) << 32;
|
272
|
+
case 4:
|
273
|
+
k1 ^= ((long) data[index + 3] & 0xff) << 24;
|
274
|
+
case 3:
|
275
|
+
k1 ^= ((long) data[index + 2] & 0xff) << 16;
|
276
|
+
case 2:
|
277
|
+
k1 ^= ((long) data[index + 1] & 0xff) << 8;
|
278
|
+
case 1:
|
279
|
+
k1 ^= data[index] & 0xff;
|
280
|
+
k1 *= C1;
|
281
|
+
k1 = Long.rotateLeft(k1, R1);
|
282
|
+
k1 *= C2;
|
283
|
+
h1 ^= k1;
|
284
|
+
}
|
285
|
+
|
286
|
+
// finalization
|
287
|
+
h1 ^= length;
|
288
|
+
h2 ^= length;
|
289
|
+
|
290
|
+
h1 += h2;
|
291
|
+
h2 += h1;
|
292
|
+
|
293
|
+
h1 = fmix64(h1);
|
294
|
+
h2 = fmix64(h2);
|
295
|
+
|
296
|
+
h1 += h2;
|
297
|
+
h2 += h1;
|
298
|
+
|
299
|
+
return new long[] { h1, h2 };
|
300
|
+
}
|
162
301
|
}
|
data/lib/splitclient-rb.rb
CHANGED
@@ -12,6 +12,8 @@ require 'splitclient-rb/cache/adapters/memory_adapter'
|
|
12
12
|
require 'splitclient-rb/cache/adapters/redis_adapter'
|
13
13
|
require 'splitclient-rb/cache/fetchers/segment_fetcher'
|
14
14
|
require 'splitclient-rb/cache/fetchers/split_fetcher'
|
15
|
+
require 'splitclient-rb/cache/hashers/impression_hasher'
|
16
|
+
require 'splitclient-rb/cache/observers/impression_observer'
|
15
17
|
require 'splitclient-rb/cache/repositories/repository'
|
16
18
|
require 'splitclient-rb/cache/repositories/segments_repository'
|
17
19
|
require 'splitclient-rb/cache/repositories/splits_repository'
|
@@ -28,6 +30,7 @@ require 'splitclient-rb/cache/senders/impressions_formatter'
|
|
28
30
|
require 'splitclient-rb/cache/senders/impressions_sender'
|
29
31
|
require 'splitclient-rb/cache/senders/metrics_sender'
|
30
32
|
require 'splitclient-rb/cache/senders/events_sender'
|
33
|
+
require 'splitclient-rb/cache/senders/impressions_count_sender'
|
31
34
|
require 'splitclient-rb/cache/senders/localhost_repo_cleaner'
|
32
35
|
require 'splitclient-rb/cache/stores/store_utils'
|
33
36
|
require 'splitclient-rb/cache/stores/localhost_split_builder'
|
@@ -52,6 +55,8 @@ require 'splitclient-rb/engine/api/metrics'
|
|
52
55
|
require 'splitclient-rb/engine/api/segments'
|
53
56
|
require 'splitclient-rb/engine/api/splits'
|
54
57
|
require 'splitclient-rb/engine/api/events'
|
58
|
+
require 'splitclient-rb/engine/common/impressions_counter'
|
59
|
+
require 'splitclient-rb/engine/common/impressions_manager'
|
55
60
|
require 'splitclient-rb/engine/parser/condition'
|
56
61
|
require 'splitclient-rb/engine/parser/partition'
|
57
62
|
require 'splitclient-rb/engine/parser/evaluator'
|
@@ -105,7 +110,7 @@ require 'splitclient-rb/sse/notification_processor'
|
|
105
110
|
require 'splitclient-rb/sse/sse_handler'
|
106
111
|
|
107
112
|
# C extension
|
108
|
-
|
113
|
+
require 'murmurhash/murmurhash_mri'
|
109
114
|
|
110
115
|
module SplitIoClient
|
111
116
|
def self.root
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
module Hashers
|
3
|
+
class ImpressionHasher
|
4
|
+
def initialize
|
5
|
+
@murmur_hash_128_64 = case RUBY_PLATFORM
|
6
|
+
when 'java'
|
7
|
+
Proc.new { |key, seed| Java::MurmurHash3.hash128x64(key, seed) }
|
8
|
+
else
|
9
|
+
Proc.new { |key, seed| Digest::MurmurHashMRI3_x64_128.rawdigest(key, [seed].pack('L')) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def process(impression)
|
14
|
+
impression_data = "#{unknown_if_null(impression[:k])}"
|
15
|
+
impression_data << ":#{unknown_if_null(impression[:f])}"
|
16
|
+
impression_data << ":#{unknown_if_null(impression[:t])}"
|
17
|
+
impression_data << ":#{unknown_if_null(impression[:r])}"
|
18
|
+
impression_data << ":#{zero_if_null(impression[:c])}"
|
19
|
+
|
20
|
+
@murmur_hash_128_64.call(impression_data, 0)[0];
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def unknown_if_null(value)
|
26
|
+
value == nil ? "UNKNOWN" : value
|
27
|
+
end
|
28
|
+
|
29
|
+
def zero_if_null(value)
|
30
|
+
value == nil ? 0 : value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
module Observers
|
3
|
+
class ImpressionObserver
|
4
|
+
LAST_SEEN_CACHE_SIZE = 500000
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@cache = LruRedux::TTL::ThreadSafeCache.new(LAST_SEEN_CACHE_SIZE)
|
8
|
+
@impression_hasher = Hashers::ImpressionHasher.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_and_set(impression)
|
12
|
+
return if impression.nil?
|
13
|
+
|
14
|
+
hash = @impression_hasher.process(impression)
|
15
|
+
previous = @cache[hash]
|
16
|
+
@cache[hash] = impression[:m]
|
17
|
+
|
18
|
+
previous.nil? ? nil : [previous, impression[:m]].min
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -10,18 +10,10 @@ module SplitIoClient
|
|
10
10
|
@adapter = @config.impressions_adapter
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
i: impression_data(
|
18
|
-
matching_key,
|
19
|
-
bucketing_key,
|
20
|
-
split_name,
|
21
|
-
treatment,
|
22
|
-
time
|
23
|
-
)
|
24
|
-
)
|
13
|
+
def add_bulk(impressions)
|
14
|
+
impressions.each do |impression|
|
15
|
+
@adapter.add_to_queue(impression)
|
16
|
+
end
|
25
17
|
rescue ThreadError # queue is full
|
26
18
|
if random_sampler.rand(1..1000) <= 2 # log only 0.2 % of the time
|
27
19
|
@config.logger.warn("Dropping impressions. Current size is \
|
@@ -30,12 +22,6 @@ module SplitIoClient
|
|
30
22
|
end
|
31
23
|
end
|
32
24
|
|
33
|
-
def add_bulk(key, bucketing_key, treatments, time)
|
34
|
-
treatments.each do |split_name, treatment|
|
35
|
-
add(key, bucketing_key, split_name, treatment, time)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
25
|
def batch
|
40
26
|
return [] if @config.impressions_bulk_size.zero?
|
41
27
|
|
@@ -12,28 +12,17 @@ module SplitIoClient
|
|
12
12
|
@adapter = @config.impressions_adapter
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def add_bulk(matching_key, bucketing_key, treatments, time)
|
20
|
-
impressions = treatments.map do |split_name, treatment|
|
21
|
-
{
|
22
|
-
m: metadata,
|
23
|
-
i: impression_data(
|
24
|
-
matching_key,
|
25
|
-
bucketing_key,
|
26
|
-
split_name,
|
27
|
-
treatment,
|
28
|
-
time
|
29
|
-
)
|
30
|
-
}.to_json
|
15
|
+
def add_bulk(impressions)
|
16
|
+
impressions_json = impressions.map do |impression|
|
17
|
+
impression.to_json
|
31
18
|
end
|
32
19
|
|
33
|
-
impressions_list_size = @adapter.add_to_queue(key,
|
20
|
+
impressions_list_size = @adapter.add_to_queue(key, impressions_json)
|
34
21
|
|
35
22
|
# Synchronizer might not be running
|
36
|
-
@adapter.expire(key, EXPIRE_SECONDS) if
|
23
|
+
@adapter.expire(key, EXPIRE_SECONDS) if impressions_json.size == impressions_list_size
|
24
|
+
rescue StandardError => e
|
25
|
+
@config.logger.error("Exception while add_bulk_v2: #{e}")
|
37
26
|
end
|
38
27
|
|
39
28
|
def get_impressions(number_of_impressions = 0)
|
@@ -6,7 +6,7 @@ module SplitIoClient
|
|
6
6
|
# Repository which forwards impressions interface to the selected adapter
|
7
7
|
class ImpressionsRepository < Repository
|
8
8
|
extend Forwardable
|
9
|
-
def_delegators :@repository, :
|
9
|
+
def_delegators :@repository, :add_bulk, :batch, :clear, :empty?
|
10
10
|
|
11
11
|
def initialize(config)
|
12
12
|
super(config)
|
@@ -17,32 +17,6 @@ module SplitIoClient
|
|
17
17
|
Repositories::Impressions::RedisRepository.new(@config)
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
21
|
-
protected
|
22
|
-
|
23
|
-
def impression_data(matching_key, bucketing_key, split_name, treatment, timestamp)
|
24
|
-
{
|
25
|
-
k: matching_key,
|
26
|
-
b: bucketing_key,
|
27
|
-
f: split_name,
|
28
|
-
t: treatment[:treatment],
|
29
|
-
r: applied_rule(treatment[:label]),
|
30
|
-
c: treatment[:change_number],
|
31
|
-
m: timestamp
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
def metadata
|
36
|
-
{
|
37
|
-
s: "#{@config.language}-#{@config.version}",
|
38
|
-
i: @config.machine_ip,
|
39
|
-
n: @config.machine_name
|
40
|
-
}
|
41
|
-
end
|
42
|
-
|
43
|
-
def applied_rule(label)
|
44
|
-
@config.labels_enabled ? label : nil
|
45
|
-
end
|
46
20
|
end
|
47
21
|
end
|
48
22
|
end
|
@@ -18,24 +18,22 @@ module SplitIoClient
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def add(impression)
|
22
|
-
enqueue(impression)
|
23
|
-
end
|
24
|
-
|
25
21
|
def add_bulk(impressions)
|
26
|
-
|
22
|
+
return unless @listener
|
23
|
+
impressions.each do |impression|
|
27
24
|
enqueue(
|
28
|
-
split_name:
|
29
|
-
matching_key:
|
30
|
-
bucketing_key:
|
31
|
-
time:
|
25
|
+
split_name: impression[:i][:f],
|
26
|
+
matching_key: impression[:i][:k],
|
27
|
+
bucketing_key: impression[:i][:b],
|
28
|
+
time: impression[:i][:m],
|
32
29
|
treatment: {
|
33
|
-
label:
|
34
|
-
treatment:
|
35
|
-
change_number:
|
30
|
+
label: impression[:i][:r],
|
31
|
+
treatment: impression[:i][:t],
|
32
|
+
change_number: impression[:i][:c]
|
36
33
|
},
|
37
|
-
|
38
|
-
|
34
|
+
previous_time: impression[:i][:pt],
|
35
|
+
attributes: impression[:attributes]
|
36
|
+
) unless impression.nil?
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module Cache
|
5
|
+
module Senders
|
6
|
+
class ImpressionsCountSender
|
7
|
+
COUNTER_REFRESH_RATE_SECONDS = 1800
|
8
|
+
|
9
|
+
def initialize(config, impression_counter, impressions_api)
|
10
|
+
@config = config
|
11
|
+
@impression_counter = impression_counter
|
12
|
+
@impressions_api = impressions_api
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
impressions_count_thread
|
17
|
+
|
18
|
+
if defined?(PhusionPassenger)
|
19
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
20
|
+
impressions_count_thread if forked
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def impressions_count_thread
|
28
|
+
@config.threads[:impressions_count_sender] = Thread.new do
|
29
|
+
begin
|
30
|
+
@config.logger.info('Starting impressions count service')
|
31
|
+
|
32
|
+
loop do
|
33
|
+
post_impressions_count
|
34
|
+
|
35
|
+
sleep(COUNTER_REFRESH_RATE_SECONDS)
|
36
|
+
end
|
37
|
+
rescue SplitIoClient::SDKShutdownException
|
38
|
+
post_impressions_count
|
39
|
+
|
40
|
+
@config.logger.info('Posting impressions count due to shutdown')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def post_impressions_count
|
45
|
+
@impressions_api.post_count(formatter(@impression_counter.pop_all))
|
46
|
+
rescue StandardError => error
|
47
|
+
@config.log_found_exception(__method__.to_s, error)
|
48
|
+
end
|
49
|
+
|
50
|
+
def formatter(counts)
|
51
|
+
return if counts.empty?
|
52
|
+
|
53
|
+
formated_counts = {pf: []}
|
54
|
+
|
55
|
+
counts.each do |key, value|
|
56
|
+
key_splited = key.split('::')
|
57
|
+
|
58
|
+
formated_counts[:pf] << {
|
59
|
+
f: key_splited[0].to_s, # feature name
|
60
|
+
m: key_splited[1].to_i, # time frame
|
61
|
+
rc: value # count
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
formated_counts
|
66
|
+
rescue StandardError => error
|
67
|
+
@config.log_found_exception(__method__.to_s, error)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -17,12 +17,10 @@ module SplitIoClient
|
|
17
17
|
|
18
18
|
formatted_impressions = unique_features(filtered_impressions).each_with_object([]) do |feature, memo|
|
19
19
|
feature_impressions = feature_impressions(filtered_impressions, feature)
|
20
|
-
ip = feature_impressions.first[:m][:i]
|
21
20
|
current_impressions = current_impressions(feature_impressions)
|
22
21
|
memo << {
|
23
|
-
|
24
|
-
|
25
|
-
ip: ip
|
22
|
+
f: feature.to_sym,
|
23
|
+
i: current_impressions
|
26
24
|
}
|
27
25
|
end
|
28
26
|
|
@@ -40,12 +38,13 @@ module SplitIoClient
|
|
40
38
|
def current_impressions(feature_impressions)
|
41
39
|
feature_impressions.map do |impression|
|
42
40
|
{
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
k: impression[:i][:k],
|
42
|
+
t: impression[:i][:t],
|
43
|
+
m: impression[:i][:m],
|
44
|
+
b: impression[:i][:b],
|
45
|
+
r: impression[:i][:r],
|
46
|
+
c: impression[:i][:c],
|
47
|
+
pt: impression[:i][:pt]
|
49
48
|
}
|
50
49
|
end
|
51
50
|
end
|
@@ -73,7 +72,8 @@ module SplitIoClient
|
|
73
72
|
"#{impression[:i][:k]}:" \
|
74
73
|
"#{impression[:i][:b]}:" \
|
75
74
|
"#{impression[:i][:c]}:" \
|
76
|
-
"#{impression[:i][:t]}"
|
75
|
+
"#{impression[:i][:t]}:" \
|
76
|
+
"#{impression[:i][:pt]}"
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
@@ -4,10 +4,10 @@ module SplitIoClient
|
|
4
4
|
module Cache
|
5
5
|
module Senders
|
6
6
|
class ImpressionsSender
|
7
|
-
def initialize(impressions_repository,
|
7
|
+
def initialize(impressions_repository, config, impressions_api)
|
8
8
|
@impressions_repository = impressions_repository
|
9
|
-
@api_key = api_key
|
10
9
|
@config = config
|
10
|
+
@impressions_api = impressions_api
|
11
11
|
end
|
12
12
|
|
13
13
|
def call
|
@@ -50,7 +50,7 @@ module SplitIoClient
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def impressions_api
|
53
|
-
@impressions_api
|
53
|
+
@impressions_api
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
@@ -9,7 +9,7 @@ module SplitIoClient
|
|
9
9
|
# @param api_key [String] the API key for your split account
|
10
10
|
#
|
11
11
|
# @return [SplitIoClient] split.io client instance
|
12
|
-
def initialize(api_key, metrics, splits_repository, segments_repository, impressions_repository, metrics_repository, events_repository, sdk_blocker, config)
|
12
|
+
def initialize(api_key, metrics, splits_repository, segments_repository, impressions_repository, metrics_repository, events_repository, sdk_blocker, config, impressions_manager)
|
13
13
|
@api_key = api_key
|
14
14
|
@metrics = metrics
|
15
15
|
@splits_repository = splits_repository
|
@@ -20,17 +20,21 @@ module SplitIoClient
|
|
20
20
|
@sdk_blocker = sdk_blocker
|
21
21
|
@destroyed = false
|
22
22
|
@config = config
|
23
|
+
@impressions_manager = impressions_manager
|
23
24
|
end
|
24
25
|
|
25
26
|
def get_treatment(
|
26
27
|
key, split_name, attributes = {}, split_data = nil, store_impressions = true,
|
27
28
|
multiple = false, evaluator = nil
|
28
29
|
)
|
29
|
-
|
30
|
+
impressions = []
|
31
|
+
result = treatment(key, split_name, attributes, split_data, store_impressions, multiple, evaluator, 'get_treatment', impressions)
|
32
|
+
@impressions_manager.track(impressions)
|
33
|
+
|
30
34
|
if multiple
|
31
|
-
|
35
|
+
result.tap { |t| t.delete(:config) }
|
32
36
|
else
|
33
|
-
|
37
|
+
result[:treatment]
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
@@ -38,7 +42,11 @@ module SplitIoClient
|
|
38
42
|
key, split_name, attributes = {}, split_data = nil, store_impressions = true,
|
39
43
|
multiple = false, evaluator = nil
|
40
44
|
)
|
41
|
-
|
45
|
+
impressions = []
|
46
|
+
result = treatment(key, split_name, attributes, split_data, store_impressions, multiple, evaluator, 'get_treatment_with_config', impressions)
|
47
|
+
@impressions_manager.track(impressions)
|
48
|
+
|
49
|
+
result
|
42
50
|
end
|
43
51
|
|
44
52
|
def get_treatments(key, split_names, attributes = {})
|
@@ -74,53 +82,6 @@ module SplitIoClient
|
|
74
82
|
@destroyed = true
|
75
83
|
end
|
76
84
|
|
77
|
-
def store_impression(split_name, matching_key, bucketing_key, treatment, attributes)
|
78
|
-
time = (Time.now.to_f * 1000.0).to_i
|
79
|
-
|
80
|
-
@impressions_repository.add(
|
81
|
-
matching_key,
|
82
|
-
bucketing_key,
|
83
|
-
split_name,
|
84
|
-
treatment,
|
85
|
-
time
|
86
|
-
)
|
87
|
-
|
88
|
-
route_impression(split_name, matching_key, bucketing_key, time, treatment, attributes)
|
89
|
-
|
90
|
-
rescue StandardError => error
|
91
|
-
@config.log_found_exception(__method__.to_s, error)
|
92
|
-
end
|
93
|
-
|
94
|
-
def route_impression(split_name, matching_key, bucketing_key, time, treatment, attributes)
|
95
|
-
impression_router.add(
|
96
|
-
split_name: split_name,
|
97
|
-
matching_key: matching_key,
|
98
|
-
bucketing_key: bucketing_key,
|
99
|
-
time: time,
|
100
|
-
treatment: {
|
101
|
-
label: treatment[:label],
|
102
|
-
treatment: treatment[:treatment],
|
103
|
-
change_number: treatment[:change_number]
|
104
|
-
},
|
105
|
-
attributes: attributes
|
106
|
-
)
|
107
|
-
end
|
108
|
-
|
109
|
-
def route_impressions(split_names, matching_key, bucketing_key, time, treatments_labels_change_numbers, attributes)
|
110
|
-
impression_router.add_bulk(
|
111
|
-
split_names: split_names,
|
112
|
-
matching_key: matching_key,
|
113
|
-
bucketing_key: bucketing_key,
|
114
|
-
time: time,
|
115
|
-
treatments_labels_change_numbers: treatments_labels_change_numbers,
|
116
|
-
attributes: attributes
|
117
|
-
)
|
118
|
-
end
|
119
|
-
|
120
|
-
def impression_router
|
121
|
-
@impression_router ||= SplitIoClient::ImpressionRouter.new(@config)
|
122
|
-
end
|
123
|
-
|
124
85
|
def track(key, traffic_type_name, event_type, value = nil, properties = nil)
|
125
86
|
return false unless valid_client && @config.split_validator.valid_track_parameters(key, traffic_type_name, event_type, value, properties)
|
126
87
|
|
@@ -239,26 +200,20 @@ module SplitIoClient
|
|
239
200
|
|
240
201
|
bucketing_key, matching_key = keys_from_key(key)
|
241
202
|
bucketing_key = bucketing_key ? bucketing_key.to_s : nil
|
242
|
-
matching_key = matching_key ? matching_key.to_s : nil
|
203
|
+
matching_key = matching_key ? matching_key.to_s : nil
|
243
204
|
|
244
205
|
evaluator = Engine::Parser::Evaluator.new(@segments_repository, @splits_repository, @config, true)
|
245
206
|
start = Time.now
|
207
|
+
impressions = []
|
246
208
|
treatments_labels_change_numbers =
|
247
209
|
@splits_repository.get_splits(sanitized_split_names).each_with_object({}) do |(name, data), memo|
|
248
|
-
memo.merge!(name => treatment(key, name, attributes, data, false, true, evaluator))
|
210
|
+
memo.merge!(name => treatment(key, name, attributes, data, false, true, evaluator, calling_method, impressions))
|
249
211
|
end
|
250
212
|
latency = (Time.now - start) * 1000.0
|
251
213
|
# Measure
|
252
214
|
@metrics.time('sdk.' + calling_method, latency)
|
253
215
|
|
254
|
-
|
255
|
-
|
256
|
-
time = (Time.now.to_f * 1000.0).to_i
|
257
|
-
@impressions_repository.add_bulk(
|
258
|
-
matching_key, bucketing_key, treatments_for_impressions, time
|
259
|
-
) unless treatments_for_impressions == {}
|
260
|
-
|
261
|
-
route_impressions(sanitized_split_names, matching_key, bucketing_key, time, treatments_for_impressions, attributes)
|
216
|
+
@impressions_manager.track(impressions)
|
262
217
|
|
263
218
|
split_names_keys = treatments_labels_change_numbers.keys
|
264
219
|
treatments = treatments_labels_change_numbers.values.map do |v|
|
@@ -284,7 +239,7 @@ module SplitIoClient
|
|
284
239
|
# @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
|
285
240
|
def treatment(
|
286
241
|
key, split_name, attributes = {}, split_data = nil, store_impressions = true,
|
287
|
-
multiple = false, evaluator = nil, calling_method = 'get_treatment'
|
242
|
+
multiple = false, evaluator = nil, calling_method = 'get_treatment', impressions = []
|
288
243
|
)
|
289
244
|
control_treatment = { treatment: Engine::Models::Treatment::CONTROL }
|
290
245
|
|
@@ -329,15 +284,18 @@ module SplitIoClient
|
|
329
284
|
end
|
330
285
|
|
331
286
|
latency = (Time.now - start) * 1000.0
|
332
|
-
|
333
|
-
|
287
|
+
|
288
|
+
impression = @impressions_manager.build_impression(matching_key, bucketing_key, split_name, treatment_data, { attributes: attributes, time: nil })
|
289
|
+
impressions << impression unless impression.nil?
|
334
290
|
|
335
291
|
# Measure
|
336
292
|
@metrics.time('sdk.' + calling_method, latency) unless multiple
|
337
293
|
rescue StandardError => error
|
294
|
+
p error
|
338
295
|
@config.log_found_exception(__method__.to_s, error)
|
339
296
|
|
340
|
-
|
297
|
+
impression = @impressions_manager.build_impression(matching_key, bucketing_key, split_name, control_treatment, { attributes: attributes, time: nil })
|
298
|
+
impressions << impression unless impression.nil?
|
341
299
|
|
342
300
|
return parsed_treatment(multiple, control_treatment.merge({ label: Engine::Models::Label::EXCEPTION }))
|
343
301
|
end
|
@@ -357,12 +315,5 @@ module SplitIoClient
|
|
357
315
|
def parsed_attributes(attributes)
|
358
316
|
return attributes || attributes.to_h
|
359
317
|
end
|
360
|
-
|
361
|
-
def get_treatment_for_impressions(treatments_labels_change_numbers)
|
362
|
-
return treatments_labels_change_numbers.select{|imp|
|
363
|
-
treatments_labels_change_numbers[imp][:label] != Engine::Models::Label::NOT_FOUND &&
|
364
|
-
!treatments_labels_change_numbers[imp][:label].nil?
|
365
|
-
}
|
366
|
-
end
|
367
318
|
end
|
368
319
|
end
|
@@ -14,16 +14,31 @@ module SplitIoClient
|
|
14
14
|
return
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
17
|
+
response = post_api("#{@config.events_uri}/testImpressions/bulk", @api_key, impressions, impressions_headers)
|
18
|
+
|
19
|
+
if response.success?
|
20
|
+
@config.split_logger.log_if_debug("Impressions reported: #{total_impressions(impressions)}")
|
21
|
+
else
|
22
|
+
@config.logger.error("Unexpected status code while posting impressions: #{response.status}." \
|
23
|
+
' - Check your API key and base URI')
|
24
|
+
raise 'Split SDK failed to connect to backend to post impressions'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def post_count(impressions_count)
|
29
|
+
if impressions_count.nil? || impressions_count[:pf].empty?
|
30
|
+
@config.split_logger.log_if_debug('No impressions count to send')
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
response = post_api("#{@config.events_uri}/testImpressions/count", @api_key, impressions_count)
|
35
|
+
|
36
|
+
if response.success?
|
37
|
+
@config.split_logger.log_if_debug("Impressions count sent: #{impressions_count[:pf].length}")
|
38
|
+
else
|
39
|
+
@config.logger.error("Unexpected status code while posting impressions count: #{response.status}." \
|
40
|
+
' - Check your API key and base URI')
|
41
|
+
raise 'Split SDK failed to connect to backend to post impressions'
|
27
42
|
end
|
28
43
|
end
|
29
44
|
|
@@ -31,14 +46,16 @@ module SplitIoClient
|
|
31
46
|
return 0 if impressions.nil?
|
32
47
|
|
33
48
|
impressions.reduce(0) do |impressions_count, impression|
|
34
|
-
impressions_count += impression[:
|
49
|
+
impressions_count += impression[:i].length
|
35
50
|
end
|
36
51
|
end
|
37
52
|
|
38
53
|
private
|
39
54
|
|
40
|
-
def
|
41
|
-
|
55
|
+
def impressions_headers
|
56
|
+
{
|
57
|
+
'SplitSDKImpressionsMode' => @config.impressions_mode.to_s
|
58
|
+
}
|
42
59
|
end
|
43
60
|
end
|
44
61
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent'
|
4
|
+
|
5
|
+
module SplitIoClient
|
6
|
+
module Engine
|
7
|
+
module Common
|
8
|
+
TIME_INTERVAL_MS = 3600 * 1000
|
9
|
+
|
10
|
+
class ImpressionCounter
|
11
|
+
DEFAULT_AMOUNT = 1
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@cache = Concurrent::Hash.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def inc(split_name, time_frame)
|
18
|
+
key = make_key(split_name, time_frame)
|
19
|
+
|
20
|
+
current_amount = @cache[key]
|
21
|
+
@cache[key] = current_amount.nil? ? DEFAULT_AMOUNT : (current_amount + DEFAULT_AMOUNT)
|
22
|
+
end
|
23
|
+
|
24
|
+
def pop_all
|
25
|
+
to_return = Concurrent::Hash.new
|
26
|
+
|
27
|
+
@cache.each do |key, value|
|
28
|
+
to_return[key] = value
|
29
|
+
end
|
30
|
+
@cache.clear
|
31
|
+
|
32
|
+
to_return
|
33
|
+
end
|
34
|
+
|
35
|
+
def truncate_time_frame(timestamp_ms)
|
36
|
+
timestamp_ms - (timestamp_ms % TIME_INTERVAL_MS)
|
37
|
+
end
|
38
|
+
|
39
|
+
def make_key(split_name, time_frame)
|
40
|
+
"#{split_name}::#{truncate_time_frame(time_frame)}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitIoClient
|
4
|
+
module Engine
|
5
|
+
module Common
|
6
|
+
class ImpressionManager
|
7
|
+
def initialize(config, impressions_repository, impression_counter)
|
8
|
+
@config = config
|
9
|
+
@impressions_repository = impressions_repository
|
10
|
+
@impression_counter = impression_counter
|
11
|
+
@impression_router = SplitIoClient::ImpressionRouter.new(@config)
|
12
|
+
@impression_observer = SplitIoClient::Observers::ImpressionObserver.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# added param time for test
|
16
|
+
def build_impression(matching_key, bucketing_key, split_name, treatment, params = {})
|
17
|
+
impression_data = impression_data(matching_key, bucketing_key, split_name, treatment, params[:time])
|
18
|
+
|
19
|
+
impression_data[:pt] = @impression_observer.test_and_set(impression_data) unless redis?
|
20
|
+
|
21
|
+
return impression_optimized(split_name, impression_data, params[:attributes]) if optimized? && !redis?
|
22
|
+
|
23
|
+
impression(impression_data, params[:attributes])
|
24
|
+
rescue StandardError => error
|
25
|
+
@config.log_found_exception(__method__.to_s, error)
|
26
|
+
end
|
27
|
+
|
28
|
+
def track(impressions)
|
29
|
+
@impressions_repository.add_bulk(impressions)
|
30
|
+
@impression_router.add_bulk(impressions)
|
31
|
+
rescue StandardError => error
|
32
|
+
@config.log_found_exception(__method__.to_s, error)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# added param time for test
|
38
|
+
def impression_data(matching_key, bucketing_key, split_name, treatment, time = nil)
|
39
|
+
{
|
40
|
+
k: matching_key,
|
41
|
+
b: bucketing_key,
|
42
|
+
f: split_name,
|
43
|
+
t: treatment[:treatment],
|
44
|
+
r: applied_rule(treatment[:label]),
|
45
|
+
c: treatment[:change_number],
|
46
|
+
m: time || (Time.now.to_f * 1000.0).to_i,
|
47
|
+
pt: nil
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def metadata
|
52
|
+
{
|
53
|
+
s: "#{@config.language}-#{@config.version}",
|
54
|
+
i: @config.machine_ip,
|
55
|
+
n: @config.machine_name
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def applied_rule(label)
|
60
|
+
@config.labels_enabled ? label : nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def optimized?
|
64
|
+
@config.impressions_mode == :optimized
|
65
|
+
end
|
66
|
+
|
67
|
+
def impression_optimized(split_name, impression_data, attributes)
|
68
|
+
@impression_counter.inc(split_name, impression_data[:m])
|
69
|
+
|
70
|
+
impression(impression_data, attributes) if should_queue_impression?(impression_data)
|
71
|
+
end
|
72
|
+
|
73
|
+
def should_queue_impression?(impression)
|
74
|
+
impression[:pt].nil? || (impression[:pt] < ((Time.now.to_f * 1000.0).to_i - Common::TIME_INTERVAL_MS))
|
75
|
+
end
|
76
|
+
|
77
|
+
def impression(impression_data, attributes)
|
78
|
+
{ m: metadata, i: impression_data, attributes: attributes }
|
79
|
+
end
|
80
|
+
|
81
|
+
def redis?
|
82
|
+
@config.impressions_adapter.class.to_s == 'SplitIoClient::Cache::Adapters::RedisAdapter'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -1,7 +1,3 @@
|
|
1
|
-
unless defined?(JRUBY_VERSION)
|
2
|
-
require 'digest/murmurhash'
|
3
|
-
end
|
4
|
-
|
5
1
|
module SplitIoClient
|
6
2
|
# Misc class in charge of providing hash functions and
|
7
3
|
# determination of treatment based on concept of buckets
|
@@ -13,7 +9,7 @@ module SplitIoClient
|
|
13
9
|
when 'java'
|
14
10
|
Proc.new { |key, seed| Java::MurmurHash3.murmurhash3_x86_32(key, seed) }
|
15
11
|
else
|
16
|
-
Proc.new { |key, seed| Digest::
|
12
|
+
Proc.new { |key, seed| Digest::MurmurHashMRI3_x86_32.rawdigest(key, [seed].pack('L')) }
|
17
13
|
end
|
18
14
|
end
|
19
15
|
|
@@ -9,14 +9,13 @@ module SplitIoClient
|
|
9
9
|
repositories,
|
10
10
|
api_key,
|
11
11
|
config,
|
12
|
-
|
13
|
-
metrics
|
12
|
+
params
|
14
13
|
)
|
15
|
-
split_fetcher = SplitFetcher.new(repositories[:splits], api_key, metrics, config, sdk_blocker)
|
16
|
-
segment_fetcher = SegmentFetcher.new(repositories[:segments], api_key, metrics, config, sdk_blocker)
|
17
|
-
sync_params = { split_fetcher: split_fetcher, segment_fetcher: segment_fetcher }
|
14
|
+
split_fetcher = SplitFetcher.new(repositories[:splits], api_key, params[:metrics], config, params[:sdk_blocker])
|
15
|
+
segment_fetcher = SegmentFetcher.new(repositories[:segments], api_key, params[:metrics], config, params[:sdk_blocker])
|
16
|
+
sync_params = { split_fetcher: split_fetcher, segment_fetcher: segment_fetcher, imp_counter: params[:impression_counter] }
|
18
17
|
|
19
|
-
@synchronizer = Synchronizer.new(repositories, api_key, config, sdk_blocker, sync_params)
|
18
|
+
@synchronizer = Synchronizer.new(repositories, api_key, config, params[:sdk_blocker], sync_params)
|
20
19
|
notification_manager_keeper = SplitIoClient::SSE::NotificationManagerKeeper.new(config) do |manager|
|
21
20
|
manager.on_occupancy { |publisher_available| process_occupancy(publisher_available) }
|
22
21
|
manager.on_push_shutdown { process_push_shutdown }
|
@@ -23,6 +23,8 @@ module SplitIoClient
|
|
23
23
|
@sdk_blocker = sdk_blocker
|
24
24
|
@split_fetcher = params[:split_fetcher]
|
25
25
|
@segment_fetcher = params[:segment_fetcher]
|
26
|
+
@impressions_api = SplitIoClient::Api::Impressions.new(@api_key, @config)
|
27
|
+
@impression_counter = params[:imp_counter]
|
26
28
|
end
|
27
29
|
|
28
30
|
def sync_all
|
@@ -35,6 +37,7 @@ module SplitIoClient
|
|
35
37
|
impressions_sender
|
36
38
|
metrics_sender
|
37
39
|
events_sender
|
40
|
+
impressions_count_sender
|
38
41
|
end
|
39
42
|
|
40
43
|
def start_periodic_fetch
|
@@ -73,7 +76,7 @@ module SplitIoClient
|
|
73
76
|
|
74
77
|
# Starts thread which loops constantly and sends impressions to the Split API
|
75
78
|
def impressions_sender
|
76
|
-
ImpressionsSender.new(@impressions_repository, @
|
79
|
+
ImpressionsSender.new(@impressions_repository, @config, @impressions_api).call
|
77
80
|
end
|
78
81
|
|
79
82
|
# Starts thread which loops constantly and sends metrics to the Split API
|
@@ -85,6 +88,11 @@ module SplitIoClient
|
|
85
88
|
def events_sender
|
86
89
|
EventsSender.new(@events_repository, @config).call
|
87
90
|
end
|
91
|
+
|
92
|
+
# Starts thread which loops constantly and sends impressions count to the Split API
|
93
|
+
def impressions_count_sender
|
94
|
+
ImpressionsCountSender.new(@config, @impression_counter, @impressions_api).call
|
95
|
+
end
|
88
96
|
end
|
89
97
|
end
|
90
98
|
end
|
@@ -53,7 +53,9 @@ module SplitIoClient
|
|
53
53
|
@segments_refresh_rate = opts[:segments_refresh_rate] || SplitConfig.default_segments_refresh_rate
|
54
54
|
@metrics_refresh_rate = opts[:metrics_refresh_rate] || SplitConfig.default_metrics_refresh_rate
|
55
55
|
|
56
|
-
@
|
56
|
+
@impressions_mode = init_impressions_mode(opts[:impressions_mode])
|
57
|
+
|
58
|
+
@impressions_refresh_rate = SplitConfig.init_impressions_refresh_rate(@impressions_mode, opts[:impressions_refresh_rate], SplitConfig.default_impressions_refresh_rate)
|
57
59
|
@impressions_queue_size = opts[:impressions_queue_size] || SplitConfig.default_impressions_queue_size
|
58
60
|
@impressions_adapter = SplitConfig.init_cache_adapter(
|
59
61
|
opts[:cache_adapter] || SplitConfig.default_cache_adapter, :queue_adapter, @impressions_queue_size, @redis_url
|
@@ -270,6 +272,30 @@ module SplitIoClient
|
|
270
272
|
|
271
273
|
attr_accessor :streaming_enabled
|
272
274
|
|
275
|
+
attr_accessor :impressions_mode
|
276
|
+
|
277
|
+
def self.default_impressions_mode
|
278
|
+
:optimized
|
279
|
+
end
|
280
|
+
|
281
|
+
def init_impressions_mode(impressions_mode)
|
282
|
+
impressions_mode ||= SplitConfig.default_impressions_mode
|
283
|
+
|
284
|
+
case impressions_mode
|
285
|
+
when :debug
|
286
|
+
return :debug
|
287
|
+
else
|
288
|
+
@logger.error('You passed an invalid impressions_mode, impressions_mode should be one of the following values: :debug or :optimized. Defaulting to :optimized mode') unless impressions_mode == :optimized
|
289
|
+
return :optimized
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def self.init_impressions_refresh_rate(impressions_mode, refresh_rate, default_rate)
|
294
|
+
return (refresh_rate.nil? || refresh_rate <= 0 ? default_rate : refresh_rate) if impressions_mode == :debug
|
295
|
+
|
296
|
+
return refresh_rate.nil? || refresh_rate <= 0 ? SplitConfig.default_impressions_refresh_rate_optimized : [default_rate, refresh_rate].max
|
297
|
+
end
|
298
|
+
|
273
299
|
def self.default_streaming_enabled
|
274
300
|
true
|
275
301
|
end
|
@@ -387,6 +413,10 @@ module SplitIoClient
|
|
387
413
|
60
|
388
414
|
end
|
389
415
|
|
416
|
+
def self.default_impressions_refresh_rate_optimized
|
417
|
+
300
|
418
|
+
end
|
419
|
+
|
390
420
|
def self.default_impression_listener_refresh_rate
|
391
421
|
0
|
392
422
|
end
|
@@ -34,10 +34,12 @@ module SplitIoClient
|
|
34
34
|
@metrics_repository = MetricsRepository.new(@config)
|
35
35
|
@sdk_blocker = SDKBlocker.new(@splits_repository, @segments_repository, @config)
|
36
36
|
@metrics = Metrics.new(100, @metrics_repository)
|
37
|
+
@impression_counter = SplitIoClient::Engine::Common::ImpressionCounter.new
|
38
|
+
@impressions_manager = SplitIoClient::Engine::Common::ImpressionManager.new(@config, @impressions_repository, @impression_counter)
|
37
39
|
|
38
40
|
start!
|
39
41
|
|
40
|
-
@client = SplitClient.new(@api_key, @metrics, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config)
|
42
|
+
@client = SplitClient.new(@api_key, @metrics, @splits_repository, @segments_repository, @impressions_repository, @metrics_repository, @events_repository, @sdk_blocker, @config, @impressions_manager)
|
41
43
|
@manager = SplitManager.new(@splits_repository, @sdk_blocker, @config)
|
42
44
|
|
43
45
|
validate_api_key
|
@@ -51,7 +53,8 @@ module SplitIoClient
|
|
51
53
|
if @config.localhost_mode
|
52
54
|
start_localhost_components
|
53
55
|
else
|
54
|
-
|
56
|
+
params = { sdk_blocker: @sdk_blocker, metrics: @metrics, impression_counter: @impression_counter }
|
57
|
+
SplitIoClient::Engine::SyncManager.new(repositories, @api_key, @config, params).start
|
55
58
|
end
|
56
59
|
end
|
57
60
|
|
data/splitclient-rb.gemspec
CHANGED
@@ -26,7 +26,14 @@ Gem::Specification.new do |spec|
|
|
26
26
|
lib/murmurhash/murmurhash.jar]
|
27
27
|
)
|
28
28
|
else
|
29
|
-
spec.
|
29
|
+
spec.files.concat(
|
30
|
+
%w[ext/murmurhash/3_x86_32.c
|
31
|
+
ext/murmurhash/3_x64_128.c
|
32
|
+
ext/murmurhash/extconf.rb
|
33
|
+
ext/murmurhash/murmurhash.c
|
34
|
+
ext/murmurhash/murmurhash.h]
|
35
|
+
)
|
36
|
+
spec.extensions = ['ext/murmurhash/extconf.rb']
|
30
37
|
end
|
31
38
|
|
32
39
|
spec.add_development_dependency 'allocation_stats'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: splitclient-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.1.4.pre.
|
4
|
+
version: 7.1.4.pre.rc8
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Split Software
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -337,6 +337,8 @@ files:
|
|
337
337
|
- lib/splitclient-rb/cache/adapters/redis_adapter.rb
|
338
338
|
- lib/splitclient-rb/cache/fetchers/segment_fetcher.rb
|
339
339
|
- lib/splitclient-rb/cache/fetchers/split_fetcher.rb
|
340
|
+
- lib/splitclient-rb/cache/hashers/impression_hasher.rb
|
341
|
+
- lib/splitclient-rb/cache/observers/impression_observer.rb
|
340
342
|
- lib/splitclient-rb/cache/repositories/events/memory_repository.rb
|
341
343
|
- lib/splitclient-rb/cache/repositories/events/redis_repository.rb
|
342
344
|
- lib/splitclient-rb/cache/repositories/events_repository.rb
|
@@ -351,6 +353,7 @@ files:
|
|
351
353
|
- lib/splitclient-rb/cache/repositories/splits_repository.rb
|
352
354
|
- lib/splitclient-rb/cache/routers/impression_router.rb
|
353
355
|
- lib/splitclient-rb/cache/senders/events_sender.rb
|
356
|
+
- lib/splitclient-rb/cache/senders/impressions_count_sender.rb
|
354
357
|
- lib/splitclient-rb/cache/senders/impressions_formatter.rb
|
355
358
|
- lib/splitclient-rb/cache/senders/impressions_sender.rb
|
356
359
|
- lib/splitclient-rb/cache/senders/localhost_repo_cleaner.rb
|
@@ -370,6 +373,8 @@ files:
|
|
370
373
|
- lib/splitclient-rb/engine/api/segments.rb
|
371
374
|
- lib/splitclient-rb/engine/api/splits.rb
|
372
375
|
- lib/splitclient-rb/engine/auth_api_client.rb
|
376
|
+
- lib/splitclient-rb/engine/common/impressions_counter.rb
|
377
|
+
- lib/splitclient-rb/engine/common/impressions_manager.rb
|
373
378
|
- lib/splitclient-rb/engine/evaluator/splitter.rb
|
374
379
|
- lib/splitclient-rb/engine/matchers/all_keys_matcher.rb
|
375
380
|
- lib/splitclient-rb/engine/matchers/between_matcher.rb
|