splitclient-rb 7.1.4.pre.rc7-java → 7.1.4.pre.rc8-java
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/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
|