From 3b213714c9cb0140da3bc7a1fb7af31b1c869512 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 28 Apr 2026 13:23:16 -0300 Subject: [PATCH 1/6] FME-15373-impressions: create impressions module skeleton with pom and empty source tree AI-Session-Id: 52375eb8-af89-45b8-bbad-1698b6636202 AI-Tool: claude-code AI-Model: unknown --- impressions/pom.xml | 59 +++++++++++++++++++++++++++++++++++++++++++++ pom.xml | 1 + 2 files changed, 60 insertions(+) create mode 100644 impressions/pom.xml diff --git a/impressions/pom.xml b/impressions/pom.xml new file mode 100644 index 00000000..372a69bf --- /dev/null +++ b/impressions/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + + io.split.client + java-client-parent + 4.18.3 + + + impressions + jar + Impressions + Impression tracking core: dedup strategies, bloom-filter unique-key tracking, counter aggregation + + + + com.google.code.gson + gson + 2.13.1 + + + org.slf4j + slf4j-api + 1.7.36 + + + com.google.guava + guava + 32.0.1-jre + + + junit + junit + test + + + org.mockito + mockito-core + 1.10.19 + test + + + org.powermock + powermock-module-junit4 + 1.7.4 + test + + + org.powermock + powermock-api-mockito + 1.7.4 + test + + + diff --git a/pom.xml b/pom.xml index c17dd8e6..5409bf6f 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,7 @@ testing okhttp-modules tracker + impressions client From 01c1841572204fd9880d04c3c7ce8b629708c755 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 28 Apr 2026 13:31:05 -0300 Subject: [PATCH 2/6] FME-15373-impressions: git mv pure-leaf impression files, DTOs, filters, and clean strategy files to impressions module - Move impression DTOs (KeyImpression, TestImpressions, ImpressionCount, DecoratedImpression, UniqueKeys) - Move core impression files (Impression, ImpressionCounter, ImpressionHasher, ImpressionObserver, ImpressionUtils, ImpressionsResult, ImpressionsSender, ImpressionsStorage*, InMemoryImpressionsStorage, ImpressionsManager, ImpressionListener, UniqueKeysTracker) - Move filters/ package wholesale (BloomFilterImp, Filter, FilterAdapter, FilterAdapterImpl) - Move clean strategy files (ProcessImpressionDebug, ProcessImpressionNone, ProcessImpressionStrategy) - Copy MurmurHash3 into impressions module to avoid targeting-engine dependency - Replace Preconditions.checkNotNull with Objects.requireNonNull in ImpressionCounter - Add hamcrest-all test dep; fix ImpressionObserverTest logger reference - impressions module builds and all moved tests pass standalone AI-Session-Id: 52375eb8-af89-45b8-bbad-1698b6636202 AI-Tool: claude-code AI-Model: unknown --- impressions/pom.xml | 6 + .../client/dtos/DecoratedImpression.java | 0 .../io/split/client/dtos/ImpressionCount.java | 0 .../io/split/client/dtos/KeyImpression.java | 0 .../io/split/client/dtos/TestImpressions.java | 0 .../java/io/split/client/dtos/UniqueKeys.java | 0 .../split/client/impressions/Impression.java | 0 .../client/impressions/ImpressionCounter.java | 4 +- .../client/impressions/ImpressionHasher.java | 0 .../impressions/ImpressionListener.java | 0 .../impressions/ImpressionObserver.java | 0 .../client/impressions/ImpressionUtils.java | 0 .../impressions/ImpressionsManager.java | 0 .../client/impressions/ImpressionsResult.java | 0 .../client/impressions/ImpressionsSender.java | 0 .../impressions/ImpressionsStorage.java | 0 .../ImpressionsStorageConsumer.java | 0 .../ImpressionsStorageProducer.java | 0 .../InMemoryImpressionsStorage.java | 0 .../client/impressions/UniqueKeysTracker.java | 0 .../impressions/filters/BloomFilterImp.java | 0 .../client/impressions/filters/Filter.java | 0 .../impressions/filters/FilterAdapter.java | 0 .../filters/FilterAdapterImpl.java | 0 .../strategy/ProcessImpressionDebug.java | 0 .../strategy/ProcessImpressionNone.java | 0 .../strategy/ProcessImpressionStrategy.java | 0 .../io/split/rules/bucketing/MurmurHash3.java | 302 ++++++++++++++++++ .../client/dtos/ImpressionCountTest.java | 0 .../split/client/dtos/KeyImpressionTest.java | 0 .../client/dtos/TestImpressionsTest.java | 0 .../io/split/client/dtos/UniqueKeysTest.java | 0 .../impressions/BloomFilterImpTest.java | 0 .../impressions/FilterAdapterImplTest.java | 0 .../impressions/ImpressionCounterTest.java | 0 .../impressions/ImpressionHasherTest.java | 0 .../impressions/ImpressionObserverTest.java | 2 +- .../impressions/ImpressionTestUtils.java | 0 .../InMemoryImpressionsStorageTest.java | 0 39 files changed, 310 insertions(+), 4 deletions(-) rename {client => impressions}/src/main/java/io/split/client/dtos/DecoratedImpression.java (100%) rename {client => impressions}/src/main/java/io/split/client/dtos/ImpressionCount.java (100%) rename {client => impressions}/src/main/java/io/split/client/dtos/KeyImpression.java (100%) rename {client => impressions}/src/main/java/io/split/client/dtos/TestImpressions.java (100%) rename {client => impressions}/src/main/java/io/split/client/dtos/UniqueKeys.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/Impression.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionCounter.java (94%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionHasher.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionListener.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionObserver.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionUtils.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionsManager.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionsResult.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionsSender.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionsStorage.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionsStorageConsumer.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionsStorageProducer.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/InMemoryImpressionsStorage.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/UniqueKeysTracker.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/filters/BloomFilterImp.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/filters/Filter.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/filters/FilterAdapter.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/filters/FilterAdapterImpl.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/strategy/ProcessImpressionDebug.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/strategy/ProcessImpressionNone.java (100%) rename {client => impressions}/src/main/java/io/split/client/impressions/strategy/ProcessImpressionStrategy.java (100%) create mode 100644 impressions/src/main/java/io/split/rules/bucketing/MurmurHash3.java rename {client => impressions}/src/test/java/io/split/client/dtos/ImpressionCountTest.java (100%) rename {client => impressions}/src/test/java/io/split/client/dtos/KeyImpressionTest.java (100%) rename {client => impressions}/src/test/java/io/split/client/dtos/TestImpressionsTest.java (100%) rename {client => impressions}/src/test/java/io/split/client/dtos/UniqueKeysTest.java (100%) rename {client => impressions}/src/test/java/io/split/client/impressions/BloomFilterImpTest.java (100%) rename {client => impressions}/src/test/java/io/split/client/impressions/FilterAdapterImplTest.java (100%) rename {client => impressions}/src/test/java/io/split/client/impressions/ImpressionCounterTest.java (100%) rename {client => impressions}/src/test/java/io/split/client/impressions/ImpressionHasherTest.java (100%) rename {client => impressions}/src/test/java/io/split/client/impressions/ImpressionObserverTest.java (99%) rename {client => impressions}/src/test/java/io/split/client/impressions/ImpressionTestUtils.java (100%) rename {client => impressions}/src/test/java/io/split/client/impressions/InMemoryImpressionsStorageTest.java (100%) diff --git a/impressions/pom.xml b/impressions/pom.xml index 372a69bf..79215437 100644 --- a/impressions/pom.xml +++ b/impressions/pom.xml @@ -37,6 +37,12 @@ junit test + + org.hamcrest + hamcrest-all + 1.3 + test + org.mockito mockito-core diff --git a/client/src/main/java/io/split/client/dtos/DecoratedImpression.java b/impressions/src/main/java/io/split/client/dtos/DecoratedImpression.java similarity index 100% rename from client/src/main/java/io/split/client/dtos/DecoratedImpression.java rename to impressions/src/main/java/io/split/client/dtos/DecoratedImpression.java diff --git a/client/src/main/java/io/split/client/dtos/ImpressionCount.java b/impressions/src/main/java/io/split/client/dtos/ImpressionCount.java similarity index 100% rename from client/src/main/java/io/split/client/dtos/ImpressionCount.java rename to impressions/src/main/java/io/split/client/dtos/ImpressionCount.java diff --git a/client/src/main/java/io/split/client/dtos/KeyImpression.java b/impressions/src/main/java/io/split/client/dtos/KeyImpression.java similarity index 100% rename from client/src/main/java/io/split/client/dtos/KeyImpression.java rename to impressions/src/main/java/io/split/client/dtos/KeyImpression.java diff --git a/client/src/main/java/io/split/client/dtos/TestImpressions.java b/impressions/src/main/java/io/split/client/dtos/TestImpressions.java similarity index 100% rename from client/src/main/java/io/split/client/dtos/TestImpressions.java rename to impressions/src/main/java/io/split/client/dtos/TestImpressions.java diff --git a/client/src/main/java/io/split/client/dtos/UniqueKeys.java b/impressions/src/main/java/io/split/client/dtos/UniqueKeys.java similarity index 100% rename from client/src/main/java/io/split/client/dtos/UniqueKeys.java rename to impressions/src/main/java/io/split/client/dtos/UniqueKeys.java diff --git a/client/src/main/java/io/split/client/impressions/Impression.java b/impressions/src/main/java/io/split/client/impressions/Impression.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/Impression.java rename to impressions/src/main/java/io/split/client/impressions/Impression.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionCounter.java b/impressions/src/main/java/io/split/client/impressions/ImpressionCounter.java similarity index 94% rename from client/src/main/java/io/split/client/impressions/ImpressionCounter.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionCounter.java index 381177a8..02ff29ac 100644 --- a/client/src/main/java/io/split/client/impressions/ImpressionCounter.java +++ b/impressions/src/main/java/io/split/client/impressions/ImpressionCounter.java @@ -5,8 +5,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import static com.google.common.base.Preconditions.checkNotNull; - public class ImpressionCounter { public static class Key { @@ -14,7 +12,7 @@ public static class Key { private final long _timeFrame; public Key(String featureFlagName, long timeframe) { - _featureName = checkNotNull(featureFlagName); + _featureName = Objects.requireNonNull(featureFlagName); _timeFrame = timeframe; } diff --git a/client/src/main/java/io/split/client/impressions/ImpressionHasher.java b/impressions/src/main/java/io/split/client/impressions/ImpressionHasher.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionHasher.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionHasher.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionListener.java b/impressions/src/main/java/io/split/client/impressions/ImpressionListener.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionListener.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionListener.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionObserver.java b/impressions/src/main/java/io/split/client/impressions/ImpressionObserver.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionObserver.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionObserver.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionUtils.java b/impressions/src/main/java/io/split/client/impressions/ImpressionUtils.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionUtils.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionUtils.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionsManager.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsManager.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionsManager.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionsManager.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionsResult.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsResult.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionsResult.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionsResult.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionsSender.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsSender.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionsSender.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionsSender.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionsStorage.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsStorage.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionsStorage.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionsStorage.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionsStorageConsumer.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsStorageConsumer.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionsStorageConsumer.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionsStorageConsumer.java diff --git a/client/src/main/java/io/split/client/impressions/ImpressionsStorageProducer.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsStorageProducer.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/ImpressionsStorageProducer.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionsStorageProducer.java diff --git a/client/src/main/java/io/split/client/impressions/InMemoryImpressionsStorage.java b/impressions/src/main/java/io/split/client/impressions/InMemoryImpressionsStorage.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/InMemoryImpressionsStorage.java rename to impressions/src/main/java/io/split/client/impressions/InMemoryImpressionsStorage.java diff --git a/client/src/main/java/io/split/client/impressions/UniqueKeysTracker.java b/impressions/src/main/java/io/split/client/impressions/UniqueKeysTracker.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/UniqueKeysTracker.java rename to impressions/src/main/java/io/split/client/impressions/UniqueKeysTracker.java diff --git a/client/src/main/java/io/split/client/impressions/filters/BloomFilterImp.java b/impressions/src/main/java/io/split/client/impressions/filters/BloomFilterImp.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/filters/BloomFilterImp.java rename to impressions/src/main/java/io/split/client/impressions/filters/BloomFilterImp.java diff --git a/client/src/main/java/io/split/client/impressions/filters/Filter.java b/impressions/src/main/java/io/split/client/impressions/filters/Filter.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/filters/Filter.java rename to impressions/src/main/java/io/split/client/impressions/filters/Filter.java diff --git a/client/src/main/java/io/split/client/impressions/filters/FilterAdapter.java b/impressions/src/main/java/io/split/client/impressions/filters/FilterAdapter.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/filters/FilterAdapter.java rename to impressions/src/main/java/io/split/client/impressions/filters/FilterAdapter.java diff --git a/client/src/main/java/io/split/client/impressions/filters/FilterAdapterImpl.java b/impressions/src/main/java/io/split/client/impressions/filters/FilterAdapterImpl.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/filters/FilterAdapterImpl.java rename to impressions/src/main/java/io/split/client/impressions/filters/FilterAdapterImpl.java diff --git a/client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionDebug.java b/impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionDebug.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionDebug.java rename to impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionDebug.java diff --git a/client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionNone.java b/impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionNone.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionNone.java rename to impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionNone.java diff --git a/client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionStrategy.java b/impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionStrategy.java similarity index 100% rename from client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionStrategy.java rename to impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionStrategy.java diff --git a/impressions/src/main/java/io/split/rules/bucketing/MurmurHash3.java b/impressions/src/main/java/io/split/rules/bucketing/MurmurHash3.java new file mode 100644 index 00000000..da0376d8 --- /dev/null +++ b/impressions/src/main/java/io/split/rules/bucketing/MurmurHash3.java @@ -0,0 +1,302 @@ +package io.split.rules.bucketing; + +/** + * The MurmurHash3 algorithm was created by Austin Appleby and placed in the public domain. + * This java port was authored by Yonik Seeley and also placed into the public domain. + * The author hereby disclaims copyright to this source code. + *

+ * This produces exactly the same hash values as the final C++ + * version of MurmurHash3 and is thus suitable for producing the same hash values across + * platforms. + *

+ * The 32 bit x86 version of this hash should be the fastest variant for relatively short keys like ids. + * murmurhash3_x64_128 is a good choice for longer strings or if you need more than 32 bits of hash. + *

+ * Note - The x86 and x64 versions do _not_ produce the same results, as the + * algorithms are optimized for their respective platforms. + *

+ * See http://github.com/yonik/java_util for future updates to this file. + */ +public final class MurmurHash3 { + + /** + * 128 bits of state + */ + public static final class LongPair { + public long val1; + public long val2; + } + + public static final int fmix32(int h) { + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + return h; + } + + public static final long fmix64(long k) { + k ^= k >>> 33; + k *= 0xff51afd7ed558ccdL; + k ^= k >>> 33; + k *= 0xc4ceb9fe1a85ec53L; + k ^= k >>> 33; + return k; + } + + /** + * Gets a long from a byte buffer in little endian byte order. + */ + public static final long getLongLittleEndian(byte[] buf, int offset) { + return ((long) buf[offset + 7] << 56) // no mask needed + | ((buf[offset + 6] & 0xffL) << 48) + | ((buf[offset + 5] & 0xffL) << 40) + | ((buf[offset + 4] & 0xffL) << 32) + | ((buf[offset + 3] & 0xffL) << 24) + | ((buf[offset + 2] & 0xffL) << 16) + | ((buf[offset + 1] & 0xffL) << 8) + | ((buf[offset] & 0xffL)); // no shift needed + } + + + /** + * Returns the MurmurHash3_x86_32 hash of the UTF-8 bytes of the String without actually encoding + * the string to a temporary buffer. This is more than 2x faster than hashing the result + * of String.getBytes(). + */ + public static long murmurhash3_x86_32(CharSequence data, int offset, int len, int seed) { + + final int c1 = 0xcc9e2d51; + final int c2 = 0x1b873593; + + int h1 = seed; + + int pos = offset; + int end = offset + len; + int k1 = 0; + int k2 = 0; + int shift = 0; + int bits = 0; + int nBytes = 0; // length in UTF8 bytes + + + while (pos < end) { + int code = data.charAt(pos++); + if (code < 0x80) { + k2 = code; + bits = 8; + + } else if (code < 0x800) { + k2 = (0xC0 | (code >> 6)) + | ((0x80 | (code & 0x3F)) << 8); + bits = 16; + } else if (code < 0xD800 || code > 0xDFFF || pos >= end) { + // we check for pos>=end to encode an unpaired surrogate as 3 bytes. + k2 = (0xE0 | (code >> 12)) + | ((0x80 | ((code >> 6) & 0x3F)) << 8) + | ((0x80 | (code & 0x3F)) << 16); + bits = 24; + } else { + // surrogate pair + // int utf32 = pos < end ? (int) data.charAt(pos++) : 0; + int utf32 = (int) data.charAt(pos++); + utf32 = ((code - 0xD7C0) << 10) + (utf32 & 0x3FF); + k2 = (0xff & (0xF0 | (utf32 >> 18))) + | ((0x80 | ((utf32 >> 12) & 0x3F))) << 8 + | ((0x80 | ((utf32 >> 6) & 0x3F))) << 16 + | (0x80 | (utf32 & 0x3F)) << 24; + bits = 32; + } + + + k1 |= k2 << shift; + + // int used_bits = 32 - shift; // how many bits of k2 were used in k1. + // int unused_bits = bits - used_bits; // (bits-(32-shift)) == bits+shift-32 == bits-newshift + + shift += bits; + if (shift >= 32) { + // mix after we have a complete word + + k1 *= c1; + k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13); + h1 = h1 * 5 + 0xe6546b64; + + shift -= 32; + // unfortunately, java won't let you shift 32 bits off, so we need to check for 0 + if (shift != 0) { + k1 = k2 >>> (bits - shift); // bits used == bits - newshift + } else { + k1 = 0; + } + nBytes += 4; + } + + } // inner + + // handle tail + if (shift > 0) { + nBytes += shift >> 3; + k1 *= c1; + k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); + k1 *= c2; + h1 ^= k1; + } + + // finalization + h1 ^= nBytes; + + // fmix(h1); + h1 ^= h1 >>> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >>> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >>> 16; + + return h1 & 0xFFFFFFFFL; + } + + // The following set of methods and constants are borrowed from: + // `This method is borrowed from `org.apache.commons.codec.digest.MurmurHash3` + + // Constants for 128-bit variant + private static final long C1 = 0x87c37b91114253d5L; + private static final long C2 = 0x4cf5ad432745937fL; + private static final int R1 = 31; + private static final int R2 = 27; + private static final int R3 = 33; + private static final int M = 5; + private static final int N1 = 0x52dce729; + private static final int N2 = 0x38495ab5; + + /** + * Gets the little-endian long from 8 bytes starting at the specified index. + * + * @param data The data + * @param index The index + * @return The little-endian long + */ + private static long getLittleEndianLong(final byte[] data, final int index) { + return (((long) data[index ] & 0xff) ) | + (((long) data[index + 1] & 0xff) << 8) | + (((long) data[index + 2] & 0xff) << 16) | + (((long) data[index + 3] & 0xff) << 24) | + (((long) data[index + 4] & 0xff) << 32) | + (((long) data[index + 5] & 0xff) << 40) | + (((long) data[index + 6] & 0xff) << 48) | + (((long) data[index + 7] & 0xff) << 56); + } + + public static long[] hash128x64(final byte[] data) { + return hash128x64(data, 0, data.length, 0); + } + + /** + * Generates 128-bit hash from the byte array with the given offset, length and seed. + * + *

This is an implementation of the 128-bit hash function {@code MurmurHash3_x64_128} + * from from Austin Applyby's original MurmurHash3 {@code c++} code in SMHasher.

+ * + * @param data The input byte array + * @param offset The first element of array + * @param length The length of array + * @param seed The initial seed value + * @return The 128-bit hash (2 longs) + */ + public static long[] hash128x64(final byte[] data, final int offset, final int length, final long seed) { + long h1 = seed; + long h2 = seed; + final int nblocks = length >> 4; + + // body + for (int i = 0; i < nblocks; i++) { + final int index = offset + (i << 4); + long k1 = getLittleEndianLong(data, index); + long k2 = getLittleEndianLong(data, index + 8); + + // mix functions for k1 + k1 *= C1; + k1 = Long.rotateLeft(k1, R1); + k1 *= C2; + h1 ^= k1; + h1 = Long.rotateLeft(h1, R2); + h1 += h2; + h1 = h1 * M + N1; + + // mix functions for k2 + k2 *= C2; + k2 = Long.rotateLeft(k2, R3); + k2 *= C1; + h2 ^= k2; + h2 = Long.rotateLeft(h2, R1); + h2 += h1; + h2 = h2 * M + N2; + } + + // tail + long k1 = 0; + long k2 = 0; + final int index = offset + (nblocks << 4); + switch (offset + length - index) { + case 15: + k2 ^= ((long) data[index + 14] & 0xff) << 48; + case 14: + k2 ^= ((long) data[index + 13] & 0xff) << 40; + case 13: + k2 ^= ((long) data[index + 12] & 0xff) << 32; + case 12: + k2 ^= ((long) data[index + 11] & 0xff) << 24; + case 11: + k2 ^= ((long) data[index + 10] & 0xff) << 16; + case 10: + k2 ^= ((long) data[index + 9] & 0xff) << 8; + case 9: + k2 ^= data[index + 8] & 0xff; + k2 *= C2; + k2 = Long.rotateLeft(k2, R3); + k2 *= C1; + h2 ^= k2; + + case 8: + k1 ^= ((long) data[index + 7] & 0xff) << 56; + case 7: + k1 ^= ((long) data[index + 6] & 0xff) << 48; + case 6: + k1 ^= ((long) data[index + 5] & 0xff) << 40; + case 5: + k1 ^= ((long) data[index + 4] & 0xff) << 32; + case 4: + k1 ^= ((long) data[index + 3] & 0xff) << 24; + case 3: + k1 ^= ((long) data[index + 2] & 0xff) << 16; + case 2: + k1 ^= ((long) data[index + 1] & 0xff) << 8; + case 1: + k1 ^= data[index] & 0xff; + k1 *= C1; + k1 = Long.rotateLeft(k1, R1); + k1 *= C2; + h1 ^= k1; + } + + // finalization + h1 ^= length; + h2 ^= length; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + h2 += h1; + + return new long[] { h1, h2 }; + } +} \ No newline at end of file diff --git a/client/src/test/java/io/split/client/dtos/ImpressionCountTest.java b/impressions/src/test/java/io/split/client/dtos/ImpressionCountTest.java similarity index 100% rename from client/src/test/java/io/split/client/dtos/ImpressionCountTest.java rename to impressions/src/test/java/io/split/client/dtos/ImpressionCountTest.java diff --git a/client/src/test/java/io/split/client/dtos/KeyImpressionTest.java b/impressions/src/test/java/io/split/client/dtos/KeyImpressionTest.java similarity index 100% rename from client/src/test/java/io/split/client/dtos/KeyImpressionTest.java rename to impressions/src/test/java/io/split/client/dtos/KeyImpressionTest.java diff --git a/client/src/test/java/io/split/client/dtos/TestImpressionsTest.java b/impressions/src/test/java/io/split/client/dtos/TestImpressionsTest.java similarity index 100% rename from client/src/test/java/io/split/client/dtos/TestImpressionsTest.java rename to impressions/src/test/java/io/split/client/dtos/TestImpressionsTest.java diff --git a/client/src/test/java/io/split/client/dtos/UniqueKeysTest.java b/impressions/src/test/java/io/split/client/dtos/UniqueKeysTest.java similarity index 100% rename from client/src/test/java/io/split/client/dtos/UniqueKeysTest.java rename to impressions/src/test/java/io/split/client/dtos/UniqueKeysTest.java diff --git a/client/src/test/java/io/split/client/impressions/BloomFilterImpTest.java b/impressions/src/test/java/io/split/client/impressions/BloomFilterImpTest.java similarity index 100% rename from client/src/test/java/io/split/client/impressions/BloomFilterImpTest.java rename to impressions/src/test/java/io/split/client/impressions/BloomFilterImpTest.java diff --git a/client/src/test/java/io/split/client/impressions/FilterAdapterImplTest.java b/impressions/src/test/java/io/split/client/impressions/FilterAdapterImplTest.java similarity index 100% rename from client/src/test/java/io/split/client/impressions/FilterAdapterImplTest.java rename to impressions/src/test/java/io/split/client/impressions/FilterAdapterImplTest.java diff --git a/client/src/test/java/io/split/client/impressions/ImpressionCounterTest.java b/impressions/src/test/java/io/split/client/impressions/ImpressionCounterTest.java similarity index 100% rename from client/src/test/java/io/split/client/impressions/ImpressionCounterTest.java rename to impressions/src/test/java/io/split/client/impressions/ImpressionCounterTest.java diff --git a/client/src/test/java/io/split/client/impressions/ImpressionHasherTest.java b/impressions/src/test/java/io/split/client/impressions/ImpressionHasherTest.java similarity index 100% rename from client/src/test/java/io/split/client/impressions/ImpressionHasherTest.java rename to impressions/src/test/java/io/split/client/impressions/ImpressionHasherTest.java diff --git a/client/src/test/java/io/split/client/impressions/ImpressionObserverTest.java b/impressions/src/test/java/io/split/client/impressions/ImpressionObserverTest.java similarity index 99% rename from client/src/test/java/io/split/client/impressions/ImpressionObserverTest.java rename to impressions/src/test/java/io/split/client/impressions/ImpressionObserverTest.java index 57fc258a..50eb338a 100644 --- a/client/src/test/java/io/split/client/impressions/ImpressionObserverTest.java +++ b/impressions/src/test/java/io/split/client/impressions/ImpressionObserverTest.java @@ -23,7 +23,7 @@ public class ImpressionObserverTest { - private static final Logger _log = LoggerFactory.getLogger(ImpressionsManagerImpl.class); + private static final Logger _log = LoggerFactory.getLogger(ImpressionObserverTest.class); // We allow the cache implementation to have a 0.01% drift in size when elements change, given that it's internal // structure/references might vary, and the ObjectSizeCalculator is not 100% accurate diff --git a/client/src/test/java/io/split/client/impressions/ImpressionTestUtils.java b/impressions/src/test/java/io/split/client/impressions/ImpressionTestUtils.java similarity index 100% rename from client/src/test/java/io/split/client/impressions/ImpressionTestUtils.java rename to impressions/src/test/java/io/split/client/impressions/ImpressionTestUtils.java diff --git a/client/src/test/java/io/split/client/impressions/InMemoryImpressionsStorageTest.java b/impressions/src/test/java/io/split/client/impressions/InMemoryImpressionsStorageTest.java similarity index 100% rename from client/src/test/java/io/split/client/impressions/InMemoryImpressionsStorageTest.java rename to impressions/src/test/java/io/split/client/impressions/InMemoryImpressionsStorageTest.java From 2061b952fd9cef2fa0f07de1e7eb388fbced1b96 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 28 Apr 2026 13:35:25 -0300 Subject: [PATCH 3/6] FME-15373-impressions: git mv AsynchronousImpressionListener; replace ThreadFactoryBuilder with inline ThreadFactory AI-Session-Id: 52375eb8-af89-45b8-bbad-1698b6636202 AI-Tool: claude-code AI-Model: unknown --- .../impressions/AsynchronousImpressionListener.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) rename {client => impressions}/src/main/java/io/split/client/impressions/AsynchronousImpressionListener.java (85%) diff --git a/client/src/main/java/io/split/client/impressions/AsynchronousImpressionListener.java b/impressions/src/main/java/io/split/client/impressions/AsynchronousImpressionListener.java similarity index 85% rename from client/src/main/java/io/split/client/impressions/AsynchronousImpressionListener.java rename to impressions/src/main/java/io/split/client/impressions/AsynchronousImpressionListener.java index 66f1d17d..9e247c7b 100644 --- a/client/src/main/java/io/split/client/impressions/AsynchronousImpressionListener.java +++ b/impressions/src/main/java/io/split/client/impressions/AsynchronousImpressionListener.java @@ -1,6 +1,5 @@ package io.split.client.impressions; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -9,6 +8,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; /** * A wrapper around an ImpressionListener provided by the customer. The purpose @@ -25,10 +25,12 @@ public class AsynchronousImpressionListener implements ImpressionListener { private final ExecutorService _executor; public static AsynchronousImpressionListener build(ImpressionListener delegate, int capacity) { - ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setDaemon(true) - .setNameFormat("impression-listener-wrapper-%d") - .build(); + AtomicInteger counter = new AtomicInteger(0); + ThreadFactory threadFactory = r -> { + Thread t = new Thread(r, "impression-listener-wrapper-" + counter.getAndIncrement()); + t.setDaemon(true); + return t; + }; ExecutorService executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(capacity), threadFactory); From 585508fc052d5a48417f55c2d3381b5c8c7d4541 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 28 Apr 2026 14:09:23 -0300 Subject: [PATCH 4/6] FME-15373-impressions: introduce UniqueKeysSender interface; decouple UniqueKeysTrackerImp from TelemetrySynchronizer - Add UniqueKeysSender interface replacing TelemetrySynchronizer dependency - git mv UniqueKeysTrackerImp to impressions module - Constructor now accepts UniqueKeysSender instead of TelemetrySynchronizer - Replace Lists.partition (Guava) with local partition() helper using subList - Replace SplitExecutorFactory with Executors.newSingleThreadScheduledExecutor - Update tests to mock UniqueKeysSender AI-Session-Id: 52375eb8-af89-45b8-bbad-1698b6636202 AI-Tool: claude-code AI-Model: unknown --- .../client/impressions/UniqueKeysSender.java | 7 + .../impressions/UniqueKeysTrackerImp.java | 48 +++-- .../impressions/UniqueKeysTrackerImpTest.java | 176 ++++++++++++++++++ 3 files changed, 211 insertions(+), 20 deletions(-) create mode 100644 impressions/src/main/java/io/split/client/impressions/UniqueKeysSender.java rename {client => impressions}/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java (83%) create mode 100644 impressions/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java diff --git a/impressions/src/main/java/io/split/client/impressions/UniqueKeysSender.java b/impressions/src/main/java/io/split/client/impressions/UniqueKeysSender.java new file mode 100644 index 00000000..a580669f --- /dev/null +++ b/impressions/src/main/java/io/split/client/impressions/UniqueKeysSender.java @@ -0,0 +1,7 @@ +package io.split.client.impressions; + +import io.split.client.dtos.UniqueKeys; + +public interface UniqueKeysSender { + void send(UniqueKeys uniqueKeys); +} diff --git a/client/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java b/impressions/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java similarity index 83% rename from client/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java rename to impressions/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java index c0034b6b..61a98f6d 100644 --- a/client/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java +++ b/impressions/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java @@ -1,13 +1,10 @@ package io.split.client.impressions; -import com.google.common.collect.Lists; import io.split.client.dtos.UniqueKeys; import io.split.client.impressions.filters.BloomFilterImp; import io.split.client.impressions.filters.Filter; import io.split.client.impressions.filters.FilterAdapter; import io.split.client.impressions.filters.FilterAdapterImpl; -import io.split.client.utils.SplitExecutorFactory; -import io.split.telemetry.synchronizer.TelemetrySynchronizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,38 +15,43 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -public class UniqueKeysTrackerImp implements UniqueKeysTracker{ +public class UniqueKeysTrackerImp implements UniqueKeysTracker { private static final Logger _log = LoggerFactory.getLogger(UniqueKeysTrackerImp.class); private static final double MARGIN_ERROR = 0.01; private static final int MAX_UNIQUE_KEYS_POST_SIZE = 5000; private static final int MAX_AMOUNT_OF_KEYS = 10000000; private final AtomicInteger trackerKeysSize = new AtomicInteger(0); private FilterAdapter filterAdapter; - private final TelemetrySynchronizer _telemetrySynchronizer; + private final UniqueKeysSender _uniqueKeysSender; private final ScheduledExecutorService _uniqueKeysSyncScheduledExecutorService; private final ScheduledExecutorService _cleanFilterScheduledExecutorService; - private final ConcurrentHashMap> uniqueKeysTracker; + private final ConcurrentHashMap> uniqueKeysTracker; private final int _uniqueKeysRefreshRate; private final int _filterRefreshRate; private final AtomicBoolean sendGuard = new AtomicBoolean(false); private static final Logger _logger = LoggerFactory.getLogger(UniqueKeysTrackerImp.class); - public UniqueKeysTrackerImp(TelemetrySynchronizer telemetrySynchronizer, int uniqueKeysRefreshRate, int filterRefreshRate, + public UniqueKeysTrackerImp(UniqueKeysSender uniqueKeysSender, int uniqueKeysRefreshRate, int filterRefreshRate, ThreadFactory threadFactory) { Filter bloomFilter = new BloomFilterImp(MAX_AMOUNT_OF_KEYS, MARGIN_ERROR); this.filterAdapter = new FilterAdapterImpl(bloomFilter); uniqueKeysTracker = new ConcurrentHashMap<>(); - _telemetrySynchronizer = telemetrySynchronizer; + _uniqueKeysSender = uniqueKeysSender; _uniqueKeysRefreshRate = uniqueKeysRefreshRate; _filterRefreshRate = filterRefreshRate; - _uniqueKeysSyncScheduledExecutorService = SplitExecutorFactory.buildSingleThreadScheduledExecutor(threadFactory,"UniqueKeys-sync-%d"); - _cleanFilterScheduledExecutorService = SplitExecutorFactory.buildSingleThreadScheduledExecutor(threadFactory,"Filter-%d"); + _uniqueKeysSyncScheduledExecutorService = threadFactory != null + ? Executors.newSingleThreadScheduledExecutor(threadFactory) + : Executors.newSingleThreadScheduledExecutor(); + _cleanFilterScheduledExecutorService = threadFactory != null + ? Executors.newSingleThreadScheduledExecutor(threadFactory) + : Executors.newSingleThreadScheduledExecutor(); } @Override @@ -66,7 +68,7 @@ public boolean track(String featureFlagName, String key) { return keysByFeature; }); _logger.debug("The feature flag " + featureFlagName + " and key " + key + " was added"); - if (trackerKeysSize.intValue() >= MAX_UNIQUE_KEYS_POST_SIZE){ + if (trackerKeysSize.intValue() >= MAX_UNIQUE_KEYS_POST_SIZE) { _logger.warn("The UniqueKeysTracker size reached the maximum limit"); try { sendUniqueKeys(); @@ -105,8 +107,8 @@ public void stop() { _cleanFilterScheduledExecutorService.shutdown(); } - public HashMap> popAll(){ - HashMap> toReturn = new HashMap<>(); + public HashMap> popAll() { + HashMap> toReturn = new HashMap<>(); for (String key : uniqueKeysTracker.keySet()) { HashSet value = uniqueKeysTracker.remove(key); toReturn.put(key, value); @@ -115,7 +117,7 @@ public HashMap> popAll(){ return toReturn; } - private void sendUniqueKeys(){ + private void sendUniqueKeys() { if (!sendGuard.compareAndSet(false, true)) { _log.debug("SendUniqueKeys already running"); return; @@ -136,7 +138,7 @@ private void sendUniqueKeys(){ uniqueKeysFromPopAll = capChunksToMaxSize(uniqueKeysFromPopAll); for (List chunk : getChunks(uniqueKeysFromPopAll)) { - _telemetrySynchronizer.synchronizeUniqueKeys(new UniqueKeys(chunk)); + _uniqueKeysSender.send(new UniqueKeys(chunk)); } } finally { sendGuard.set(false); @@ -147,7 +149,7 @@ private List capChunksToMaxSize(List List finalChunk = new ArrayList<>(); for (UniqueKeys.UniqueKey uniqueKey : uniqueKeys) { if (uniqueKey.keysDto.size() > MAX_UNIQUE_KEYS_POST_SIZE) { - for(List subChunk : Lists.partition(uniqueKey.keysDto, MAX_UNIQUE_KEYS_POST_SIZE)) { + for (List subChunk : partition(uniqueKey.keysDto, MAX_UNIQUE_KEYS_POST_SIZE)) { finalChunk.add(new UniqueKeys.UniqueKey(uniqueKey.featureName, subChunk)); } continue; @@ -157,6 +159,14 @@ private List capChunksToMaxSize(List return finalChunk; } + private static List> partition(List list, int size) { + List> result = new ArrayList<>(); + for (int i = 0; i < list.size(); i += size) { + result.add(list.subList(i, Math.min(i + size, list.size()))); + } + return result; + } + private List> getChunks(List uniqueKeys) { List> chunks = new ArrayList<>(); List intermediateChunk = new ArrayList<>(); @@ -180,13 +190,12 @@ private int getChunkSize(List uniqueKeysChunk) { } return totalSize; } - - private interface ExecuteUniqueKeysAction{ + + private interface ExecuteUniqueKeysAction { void execute(); } private class ExecuteCleanFilter implements ExecuteUniqueKeysAction { - @Override public void execute() { filterAdapter.clear(); @@ -194,7 +203,6 @@ public void execute() { } private class ExecuteSendUniqueKeys implements ExecuteUniqueKeysAction { - @Override public void execute() { sendUniqueKeys(); diff --git a/impressions/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java b/impressions/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java new file mode 100644 index 00000000..e769390f --- /dev/null +++ b/impressions/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java @@ -0,0 +1,176 @@ +package io.split.client.impressions; + +import io.split.client.dtos.UniqueKeys; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +public class UniqueKeysTrackerImpTest { + private static UniqueKeysSender _uniqueKeysSender = Mockito.mock(UniqueKeysSender.class); + + @Test + public void addSomeElements(){ + UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(_uniqueKeysSender, 10000, 10000, null); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key3")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key4")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key5")); + + HashMap> result = uniqueKeysTrackerImp.popAll(); + Assert.assertEquals(2,result.size()); + + HashSet value1 = result.get("feature1"); + Assert.assertEquals(3,value1.size()); + Assert.assertTrue(value1.contains("key1")); + Assert.assertTrue(value1.contains("key2")); + Assert.assertTrue(value1.contains("key3")); + + HashSet value2 = result.get("feature2"); + Assert.assertEquals(2,value2.size()); + Assert.assertTrue(value2.contains("key4")); + Assert.assertTrue(value2.contains("key5")); + } + + @Test + public void addTheSameElements(){ + UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(_uniqueKeysSender, 10000, 10000, null); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key3")); + + Assert.assertFalse(uniqueKeysTrackerImp.track("feature1","key1")); + Assert.assertFalse(uniqueKeysTrackerImp.track("feature1","key2")); + Assert.assertFalse(uniqueKeysTrackerImp.track("feature1","key3")); + + HashMap> result = uniqueKeysTrackerImp.popAll(); + Assert.assertEquals(1,result.size()); + + HashSet value1 = result.get("feature1"); + Assert.assertEquals(3,value1.size()); + Assert.assertTrue(value1.contains("key1")); + Assert.assertTrue(value1.contains("key2")); + Assert.assertTrue(value1.contains("key3")); + } + + @Test + public void popAllUniqueKeys(){ + UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(_uniqueKeysSender, 10000, 10000, null); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key3")); + + HashMap> result = uniqueKeysTrackerImp.popAll(); + Assert.assertEquals(2,result.size()); + HashMap> resultAfterPopAll = uniqueKeysTrackerImp.popAll(); + Assert.assertEquals(0,resultAfterPopAll.size()); + } + + @Test + public void testSynchronization() throws Exception { + UniqueKeysSender sender = Mockito.mock(UniqueKeysSender.class); + UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(sender, 1, 3, null); + uniqueKeysTrackerImp.start(); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key3")); + + Thread.sleep(2900); + Mockito.verify(sender, Mockito.times(1)).send(Mockito.anyObject()); + Thread.sleep(2900); + Mockito.verify(sender, Mockito.times(1)).send(Mockito.anyObject()); + } + + @Test + public void testStopSynchronization() throws Exception { + UniqueKeysSender sender = Mockito.mock(UniqueKeysSender.class); + UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(sender, 1, 2, null); + uniqueKeysTrackerImp.start(); + Assert.assertFalse(uniqueKeysTrackerImp.getSendGuard().get()); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key3")); + + Thread.sleep(2100); + Mockito.verify(sender, Mockito.times(1)).send(Mockito.anyObject()); + uniqueKeysTrackerImp.stop(); + Mockito.verify(sender, Mockito.times(1)).send(Mockito.anyObject()); + } + + @Test + public void testUniqueKeysChunks() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(_uniqueKeysSender, 10000, 10000, null); + HashMap> uniqueKeysHashMap = new HashMap<>(); + HashSet feature1 = new HashSet<>(); + HashSet feature2 = new HashSet<>(); + HashSet feature3 = new HashSet<>(); + HashSet feature4 = new HashSet<>(); + HashSet feature5 = new HashSet<>(); + for (Integer i=1; i<6000; i++) { + if (i <= 1000) { + feature1.add("key" + i); + } + if (i <= 2000) { + feature2.add("key" + i); + } + if (i <= 3000) { + feature3.add("key" + i); + } + if (i <= 4000) { + feature4.add("key" + i); + } + feature5.add("key" + i); + } + uniqueKeysHashMap.put("feature1", feature1); + uniqueKeysHashMap.put("feature2", feature2); + uniqueKeysHashMap.put("feature3", feature3); + uniqueKeysHashMap.put("feature4", feature4); + uniqueKeysHashMap.put("feature5", feature5); + + List uniqueKeysFromPopAll = new ArrayList<>(); + for (Map.Entry> uniqueKeyEntry : uniqueKeysHashMap.entrySet()) { + UniqueKeys.UniqueKey uniqueKey = new UniqueKeys.UniqueKey(uniqueKeyEntry.getKey(), new ArrayList<>(uniqueKeyEntry.getValue())); + uniqueKeysFromPopAll.add(uniqueKey); + } + Method methodCapChunks = uniqueKeysTrackerImp.getClass().getDeclaredMethod("capChunksToMaxSize", List.class); + methodCapChunks.setAccessible(true); + uniqueKeysFromPopAll = (List)methodCapChunks.invoke(uniqueKeysTrackerImp, uniqueKeysFromPopAll); + + Method methodGetChunks = uniqueKeysTrackerImp.getClass().getDeclaredMethod("getChunks", List.class); + methodGetChunks.setAccessible(true); + List> keysChunks = (List>) methodGetChunks.invoke(uniqueKeysTrackerImp, uniqueKeysFromPopAll); + for (List chunk : keysChunks) { + int chunkSize = 0; + for (UniqueKeys.UniqueKey keys : chunk) { + chunkSize += keys.keysDto.size(); + } + Assert.assertTrue(chunkSize <= 5000); + } + } + + @Test + public void testTrackReachMaxKeys() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { + UniqueKeysSender sender = Mockito.mock(UniqueKeysSender.class); + UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(sender, 10000, 10000, null); + for (int i=1; i<6000; i++) { + Assert.assertTrue(uniqueKeysTrackerImp.track("feature1", "key" + i)); + Assert.assertTrue(uniqueKeysTrackerImp.track("feature2", "key" + i)); + } + Mockito.verify(sender, Mockito.times(2)).send(Mockito.anyObject()); + + Field getTrackerSize = uniqueKeysTrackerImp.getClass().getDeclaredField("trackerKeysSize"); + getTrackerSize.setAccessible(true); + AtomicInteger trackerSize = (AtomicInteger) getTrackerSize.get(uniqueKeysTrackerImp); + Assert.assertTrue(trackerSize.intValue() == 1998); + } +} From 411c85878862a808f65f2a0503d1fda0fec1e510 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 28 Apr 2026 14:34:31 -0300 Subject: [PATCH 5/6] FME-15373-impressions: introduce ImpressionsManagerConfig and ImpressionsTelemetryRecorder; decouple ImpressionsManagerImpl and ProcessImpressionOptimized - Add ImpressionsManagerConfig (replaces SplitClientConfig reads) - Add ImpressionsTelemetryRecorder interface + NoopImpressionsTelemetryRecorder - git mv ImpressionsManagerImpl: accepts ImpressionsManagerConfig + ImpressionsTelemetryRecorder, removes SplitClientConfig/TelemetryRuntimeProducer/SplitExecutorFactory deps, inlines scheduler with Executors.newScheduledThreadPool, removes @VisibleForTesting/checkNotNull - git mv ProcessImpressionOptimized: uses ImpressionsTelemetryRecorder instead of TelemetryRuntimeProducer - git mv ImpressionsManagerImplTest: updated all 25 tests to use new abstractions AI-Session-Id: 52375eb8-af89-45b8-bbad-1698b6636202 AI-Tool: claude-code AI-Model: unknown --- .../impressions/ImpressionsManagerConfig.java | 39 ++ .../impressions/ImpressionsManagerImpl.java | 149 ++++---- .../ImpressionsTelemetryRecorder.java | 6 + .../NoopImpressionsTelemetryRecorder.java | 9 + .../strategy/ProcessImpressionOptimized.java | 26 +- .../ImpressionsManagerImplTest.java | 336 +++++++----------- 6 files changed, 263 insertions(+), 302 deletions(-) create mode 100644 impressions/src/main/java/io/split/client/impressions/ImpressionsManagerConfig.java rename {client => impressions}/src/main/java/io/split/client/impressions/ImpressionsManagerImpl.java (51%) create mode 100644 impressions/src/main/java/io/split/client/impressions/ImpressionsTelemetryRecorder.java create mode 100644 impressions/src/main/java/io/split/client/impressions/NoopImpressionsTelemetryRecorder.java rename {client => impressions}/src/main/java/io/split/client/impressions/strategy/ProcessImpressionOptimized.java (70%) rename {client => impressions}/src/test/java/io/split/client/impressions/ImpressionsManagerImplTest.java (80%) diff --git a/impressions/src/main/java/io/split/client/impressions/ImpressionsManagerConfig.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsManagerConfig.java new file mode 100644 index 00000000..d3b9e1b9 --- /dev/null +++ b/impressions/src/main/java/io/split/client/impressions/ImpressionsManagerConfig.java @@ -0,0 +1,39 @@ +package io.split.client.impressions; + +import java.util.concurrent.ThreadFactory; + +public final class ImpressionsManagerConfig { + + private final ImpressionsManager.Mode _mode; + private final int _impressionsRefreshRateSeconds; + private final ThreadFactory _threadFactory; + private final boolean _debugEnabled; + + private ImpressionsManagerConfig(Builder builder) { + _mode = builder._mode; + _impressionsRefreshRateSeconds = builder._impressionsRefreshRateSeconds; + _threadFactory = builder._threadFactory; + _debugEnabled = builder._debugEnabled; + } + + public ImpressionsManager.Mode mode() { return _mode; } + public int impressionsRefreshRateSeconds() { return _impressionsRefreshRateSeconds; } + public ThreadFactory threadFactory() { return _threadFactory; } + public boolean debugEnabled() { return _debugEnabled; } + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private ImpressionsManager.Mode _mode = ImpressionsManager.Mode.OPTIMIZED; + private int _impressionsRefreshRateSeconds = 60; + private ThreadFactory _threadFactory = null; + private boolean _debugEnabled = false; + + public Builder mode(ImpressionsManager.Mode mode) { _mode = mode; return this; } + public Builder impressionsRefreshRateSeconds(int rate) { _impressionsRefreshRateSeconds = rate; return this; } + public Builder threadFactory(ThreadFactory tf) { _threadFactory = tf; return this; } + public Builder debugEnabled(boolean debug) { _debugEnabled = debug; return this; } + + public ImpressionsManagerConfig build() { return new ImpressionsManagerConfig(this); } + } +} diff --git a/client/src/main/java/io/split/client/impressions/ImpressionsManagerImpl.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsManagerImpl.java similarity index 51% rename from client/src/main/java/io/split/client/impressions/ImpressionsManagerImpl.java rename to impressions/src/main/java/io/split/client/impressions/ImpressionsManagerImpl.java index 3b784aba..f9f7d734 100644 --- a/client/src/main/java/io/split/client/impressions/ImpressionsManagerImpl.java +++ b/impressions/src/main/java/io/split/client/impressions/ImpressionsManagerImpl.java @@ -1,33 +1,24 @@ package io.split.client.impressions; -import com.google.common.annotations.VisibleForTesting; -import io.split.client.SplitClientConfig; import io.split.client.dtos.DecoratedImpression; import io.split.client.dtos.KeyImpression; import io.split.client.dtos.TestImpressions; import io.split.client.impressions.strategy.ProcessImpressionNone; import io.split.client.impressions.strategy.ProcessImpressionStrategy; -import io.split.client.utils.SplitExecutorFactory; -import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum; -import io.split.telemetry.storage.TelemetryRuntimeProducer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Created by patricioe on 6/17/16. - */ public class ImpressionsManagerImpl implements ImpressionsManager, Closeable { private static final Logger _log = LoggerFactory.getLogger(ImpressionsManagerImpl.class); @@ -35,89 +26,89 @@ public class ImpressionsManagerImpl implements ImpressionsManager, Closeable { private static final long BULK_INITIAL_DELAY_SECONDS = 10L; private static final long COUNT_INITIAL_DELAY_SECONDS = 100L; private static final long COUNT_REFRESH_RATE_SECONDS = 30 * 60; - private final SplitClientConfig _config; + + private final ImpressionsManagerConfig _config; private final ImpressionsStorageProducer _impressionsStorageProducer; private final ImpressionsStorageConsumer _impressionsStorageConsumer; private final ScheduledExecutorService _scheduler; private final ImpressionsSender _impressionsSender; private final ImpressionListener _listener; private final ImpressionsManager.Mode _impressionsMode; - private TelemetryRuntimeProducer _telemetryRuntimeProducer; + private final ImpressionsTelemetryRecorder _telemetryRecorder; private ImpressionCounter _counter; private ProcessImpressionStrategy _processImpressionStrategy; private ProcessImpressionNone _processImpressionNone; - private final int _impressionsRefreshRate; - - public static ImpressionsManagerImpl instance(SplitClientConfig config, - TelemetryRuntimeProducer telemetryRuntimeProducer, - ImpressionsStorageConsumer impressionsStorageConsumer, - ImpressionsStorageProducer impressionsStorageProducer, - ImpressionsSender impressionsSender, - ProcessImpressionNone processImpressionNone, - ProcessImpressionStrategy processImpressionStrategy, - ImpressionCounter counter, - ImpressionListener listener) throws URISyntaxException { - return new ImpressionsManagerImpl(config, impressionsSender, telemetryRuntimeProducer, impressionsStorageConsumer, + public static ImpressionsManagerImpl instance(ImpressionsManagerConfig config, + ImpressionsTelemetryRecorder telemetryRecorder, + ImpressionsStorageConsumer impressionsStorageConsumer, + ImpressionsStorageProducer impressionsStorageProducer, + ImpressionsSender impressionsSender, + ProcessImpressionNone processImpressionNone, + ProcessImpressionStrategy processImpressionStrategy, + ImpressionCounter counter, + ImpressionListener listener) { + return new ImpressionsManagerImpl(config, impressionsSender, telemetryRecorder, impressionsStorageConsumer, impressionsStorageProducer, processImpressionNone, processImpressionStrategy, counter, listener); } - public static ImpressionsManagerImpl instanceForTest(SplitClientConfig config, - ImpressionsSender impressionsSender, - TelemetryRuntimeProducer telemetryRuntimeProducer, - ImpressionsStorageConsumer impressionsStorageConsumer, - ImpressionsStorageProducer impressionsStorageProducer, - ProcessImpressionNone processImpressionNone, - ProcessImpressionStrategy processImpressionStrategy, - ImpressionCounter counter, - ImpressionListener listener) { - return new ImpressionsManagerImpl(config, impressionsSender, telemetryRuntimeProducer, impressionsStorageConsumer, + public static ImpressionsManagerImpl instanceForTest(ImpressionsManagerConfig config, + ImpressionsSender impressionsSender, + ImpressionsTelemetryRecorder telemetryRecorder, + ImpressionsStorageConsumer impressionsStorageConsumer, + ImpressionsStorageProducer impressionsStorageProducer, + ProcessImpressionNone processImpressionNone, + ProcessImpressionStrategy processImpressionStrategy, + ImpressionCounter counter, + ImpressionListener listener) { + return new ImpressionsManagerImpl(config, impressionsSender, telemetryRecorder, impressionsStorageConsumer, impressionsStorageProducer, processImpressionNone, processImpressionStrategy, counter, listener); } - private ImpressionsManagerImpl(SplitClientConfig config, - ImpressionsSender impressionsSender, - TelemetryRuntimeProducer telemetryRuntimeProducer, - ImpressionsStorageConsumer impressionsStorageConsumer, - ImpressionsStorageProducer impressionsStorageProducer, - ProcessImpressionNone processImpressionNone, - ProcessImpressionStrategy processImpressionStrategy, - ImpressionCounter impressionCounter, - ImpressionListener impressionListener) { - - - _config = checkNotNull(config); - _impressionsMode = checkNotNull(config.impressionsMode()); - _impressionsStorageConsumer = checkNotNull(impressionsStorageConsumer); - _impressionsStorageProducer = checkNotNull(impressionsStorageProducer); - _telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer); - _processImpressionNone = checkNotNull(processImpressionNone); - _processImpressionStrategy = checkNotNull(processImpressionStrategy); + private ImpressionsManagerImpl(ImpressionsManagerConfig config, + ImpressionsSender impressionsSender, + ImpressionsTelemetryRecorder telemetryRecorder, + ImpressionsStorageConsumer impressionsStorageConsumer, + ImpressionsStorageProducer impressionsStorageProducer, + ProcessImpressionNone processImpressionNone, + ProcessImpressionStrategy processImpressionStrategy, + ImpressionCounter impressionCounter, + ImpressionListener impressionListener) { + _config = Objects.requireNonNull(config); + _impressionsMode = Objects.requireNonNull(config.mode()); + _impressionsStorageConsumer = Objects.requireNonNull(impressionsStorageConsumer); + _impressionsStorageProducer = Objects.requireNonNull(impressionsStorageProducer); + _telemetryRecorder = Objects.requireNonNull(telemetryRecorder); + _processImpressionNone = Objects.requireNonNull(processImpressionNone); + _processImpressionStrategy = Objects.requireNonNull(processImpressionStrategy); _impressionsSender = impressionsSender; _counter = impressionCounter; - _scheduler = SplitExecutorFactory.buildScheduledExecutorService(config.getThreadFactory(), "Split-ImpressionsManager-%d", 2); + ThreadFactory tf = config.threadFactory(); + _scheduler = tf != null + ? Executors.newScheduledThreadPool(2, tf) + : Executors.newScheduledThreadPool(2); _listener = impressionListener; - - _impressionsRefreshRate = config.impressionsRefreshRate(); } @Override - public void start(){ - switch (_impressionsMode){ + public void start() { + switch (_impressionsMode) { case OPTIMIZED: - _scheduler.scheduleAtFixedRate(this::sendImpressionCounters, COUNT_INITIAL_DELAY_SECONDS, COUNT_REFRESH_RATE_SECONDS, - TimeUnit.SECONDS); - _scheduler.scheduleAtFixedRate(this::sendImpressions, BULK_INITIAL_DELAY_SECONDS, _impressionsRefreshRate, TimeUnit.SECONDS); + _scheduler.scheduleAtFixedRate(this::sendImpressionCounters, COUNT_INITIAL_DELAY_SECONDS, + COUNT_REFRESH_RATE_SECONDS, TimeUnit.SECONDS); + _scheduler.scheduleAtFixedRate(this::sendImpressions, BULK_INITIAL_DELAY_SECONDS, + _config.impressionsRefreshRateSeconds(), TimeUnit.SECONDS); break; case DEBUG: - _scheduler.scheduleAtFixedRate(this::sendImpressions, BULK_INITIAL_DELAY_SECONDS, _impressionsRefreshRate, TimeUnit.SECONDS); - _scheduler.scheduleAtFixedRate(this::sendImpressionCounters, COUNT_INITIAL_DELAY_SECONDS, COUNT_REFRESH_RATE_SECONDS, - TimeUnit.SECONDS); + _scheduler.scheduleAtFixedRate(this::sendImpressions, BULK_INITIAL_DELAY_SECONDS, + _config.impressionsRefreshRateSeconds(), TimeUnit.SECONDS); + _scheduler.scheduleAtFixedRate(this::sendImpressionCounters, COUNT_INITIAL_DELAY_SECONDS, + COUNT_REFRESH_RATE_SECONDS, TimeUnit.SECONDS); break; case NONE: - _scheduler.scheduleAtFixedRate(this::sendImpressionCounters, COUNT_INITIAL_DELAY_SECONDS, COUNT_REFRESH_RATE_SECONDS, - TimeUnit.SECONDS); + _scheduler.scheduleAtFixedRate(this::sendImpressionCounters, COUNT_INITIAL_DELAY_SECONDS, + COUNT_REFRESH_RATE_SECONDS, TimeUnit.SECONDS); break; } } @@ -142,17 +133,19 @@ public void track(List decoratedImpressions) { if (!Objects.isNull(impressionsResult.getImpressionsToQueue())) { impressionsForLogs.addAll(impressionsResult.getImpressionsToQueue()); } - if (!Objects.isNull(impressionsResult.getImpressionsToListener())) + if (!Objects.isNull(impressionsResult.getImpressionsToListener())) { impressionsToListener.addAll(impressionsResult.getImpressionsToListener()); + } } int totalImpressions = impressionsForLogs.size(); - long queued = _impressionsStorageProducer.put(impressionsForLogs.stream().map(KeyImpression::fromImpression).collect(Collectors.toList())); + long queued = _impressionsStorageProducer.put( + impressionsForLogs.stream().map(KeyImpression::fromImpression).collect(Collectors.toList())); if (queued < totalImpressions) { - _telemetryRuntimeProducer.recordImpressionStats(ImpressionsDataTypeEnum.IMPRESSIONS_DROPPED, totalImpressions-queued); + _telemetryRecorder.recordImpressionsDropped(totalImpressions - queued); } - _telemetryRuntimeProducer.recordImpressionStats(ImpressionsDataTypeEnum.IMPRESSIONS_QUEUED, queued); + _telemetryRecorder.recordImpressionsQueued(queued); - if (_listener!=null){ + if (_listener != null) { impressionsToListener.forEach(imp -> _listener.log(imp)); } } @@ -160,22 +153,20 @@ public void track(List decoratedImpressions) { @Override public void close() { try { - if(_listener!= null){ + if (_listener != null) { _listener.close(); _log.info("Successful shutdown of ImpressionListener"); } _scheduler.shutdown(); sendImpressions(); - if(_counter != null) { + if (_counter != null) { sendImpressionCounters(); } } catch (Exception e) { _log.warn("Unable to close ImpressionsManager properly", e); } - } - @VisibleForTesting /* package private */ void sendImpressions() { if (_impressionsStorageConsumer.isFull()) { _log.warn("Split SDK impressions queue is full. Impressions may have been dropped. Consider increasing capacity."); @@ -184,7 +175,7 @@ public void close() { long start = System.currentTimeMillis(); List impressions = _impressionsStorageConsumer.pop(); if (impressions.isEmpty()) { - return; // Nothing to send + return; } _impressionsSender.postImpressionsBulk(TestImpressions.fromKeyImpressions(impressions)); @@ -194,15 +185,13 @@ public void close() { } } - @VisibleForTesting - /* package private */ void sendImpressionCounters() { + /* package private */ void sendImpressionCounters() { if (!_counter.isEmpty()) { _impressionsSender.postCounters(_counter.popAll()); } } - @VisibleForTesting /* package private */ ImpressionCounter getCounter() { return _counter; } -} \ No newline at end of file +} diff --git a/impressions/src/main/java/io/split/client/impressions/ImpressionsTelemetryRecorder.java b/impressions/src/main/java/io/split/client/impressions/ImpressionsTelemetryRecorder.java new file mode 100644 index 00000000..12603e8f --- /dev/null +++ b/impressions/src/main/java/io/split/client/impressions/ImpressionsTelemetryRecorder.java @@ -0,0 +1,6 @@ +package io.split.client.impressions; + +public interface ImpressionsTelemetryRecorder { + void recordImpressionsDropped(long count); + void recordImpressionsQueued(long count); +} diff --git a/impressions/src/main/java/io/split/client/impressions/NoopImpressionsTelemetryRecorder.java b/impressions/src/main/java/io/split/client/impressions/NoopImpressionsTelemetryRecorder.java new file mode 100644 index 00000000..8f6da987 --- /dev/null +++ b/impressions/src/main/java/io/split/client/impressions/NoopImpressionsTelemetryRecorder.java @@ -0,0 +1,9 @@ +package io.split.client.impressions; + +public class NoopImpressionsTelemetryRecorder implements ImpressionsTelemetryRecorder { + @Override + public void recordImpressionsDropped(long count) {} + + @Override + public void recordImpressionsQueued(long count) {} +} diff --git a/client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionOptimized.java b/impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionOptimized.java similarity index 70% rename from client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionOptimized.java rename to impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionOptimized.java index 65fd9ab4..99c6fc55 100644 --- a/client/src/main/java/io/split/client/impressions/strategy/ProcessImpressionOptimized.java +++ b/impressions/src/main/java/io/split/client/impressions/strategy/ProcessImpressionOptimized.java @@ -1,28 +1,27 @@ package io.split.client.impressions.strategy; import io.split.client.impressions.Impression; +import io.split.client.impressions.ImpressionCounter; import io.split.client.impressions.ImpressionObserver; -import io.split.client.impressions.ImpressionUtils; import io.split.client.impressions.ImpressionsResult; -import io.split.client.impressions.ImpressionCounter; -import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum; -import io.split.telemetry.storage.TelemetryRuntimeProducer; +import io.split.client.impressions.ImpressionsTelemetryRecorder; +import io.split.client.impressions.ImpressionUtils; import java.util.ArrayList; import java.util.List; import java.util.Objects; -public class ProcessImpressionOptimized implements ProcessImpressionStrategy{ +public class ProcessImpressionOptimized implements ProcessImpressionStrategy { private final ImpressionObserver _impressionObserver; private final ImpressionCounter _impressionCounter; - private final TelemetryRuntimeProducer _telemetryRuntimeProducer; + private final ImpressionsTelemetryRecorder _telemetryRecorder; private final boolean _listenerEnabled; - - public ProcessImpressionOptimized(boolean listenerEnabled, ImpressionObserver impressionObserver, ImpressionCounter impressionCounter, - TelemetryRuntimeProducer telemetryRuntimeProducer) { - _telemetryRuntimeProducer = telemetryRuntimeProducer; + public ProcessImpressionOptimized(boolean listenerEnabled, ImpressionObserver impressionObserver, + ImpressionCounter impressionCounter, + ImpressionsTelemetryRecorder telemetryRecorder) { + _telemetryRecorder = telemetryRecorder; _listenerEnabled = listenerEnabled; _impressionObserver = impressionObserver; _impressionCounter = impressionCounter; @@ -31,7 +30,7 @@ public ProcessImpressionOptimized(boolean listenerEnabled, ImpressionObserver im @Override public ImpressionsResult process(List impressions) { List impressionsToQueue = new ArrayList<>(); - for(Impression impression : impressions) { + for (Impression impression : impressions) { if (impression.properties() == null) { impression = impression.withPreviousTime(_impressionObserver.testAndSet(impression)); if (!Objects.isNull(impression.pt()) && impression.pt() != 0) { @@ -43,10 +42,9 @@ public ImpressionsResult process(List impressions) { } impressionsToQueue.add(impression); } - List impressionForListener = this._listenerEnabled ? impressions : null; + List impressionForListener = this._listenerEnabled ? impressions : null; - _telemetryRuntimeProducer.recordImpressionStats(ImpressionsDataTypeEnum.IMPRESSIONS_DEDUPED, impressions.size()- - (long)impressionsToQueue.size()); + _telemetryRecorder.recordImpressionsDropped(impressions.size() - (long) impressionsToQueue.size()); return new ImpressionsResult(impressionsToQueue, impressionForListener); } diff --git a/client/src/test/java/io/split/client/impressions/ImpressionsManagerImplTest.java b/impressions/src/test/java/io/split/client/impressions/ImpressionsManagerImplTest.java similarity index 80% rename from client/src/test/java/io/split/client/impressions/ImpressionsManagerImplTest.java rename to impressions/src/test/java/io/split/client/impressions/ImpressionsManagerImplTest.java index 6dbf5061..29a358d5 100644 --- a/client/src/test/java/io/split/client/impressions/ImpressionsManagerImplTest.java +++ b/impressions/src/test/java/io/split/client/impressions/ImpressionsManagerImplTest.java @@ -1,6 +1,5 @@ package io.split.client.impressions; -import io.split.client.SplitClientConfig; import io.split.client.dtos.DecoratedImpression; import io.split.client.dtos.KeyImpression; import io.split.client.dtos.TestImpressions; @@ -9,14 +8,9 @@ import io.split.client.impressions.strategy.ProcessImpressionDebug; import io.split.client.impressions.strategy.ProcessImpressionNone; import io.split.client.impressions.strategy.ProcessImpressionOptimized; +import io.split.client.impressions.ImpressionsManagerConfig; +import io.split.client.impressions.ImpressionsTelemetryRecorder; import io.split.client.impressions.strategy.ProcessImpressionStrategy; -import io.split.storages.enums.OperationMode; -import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum; -import io.split.telemetry.storage.InMemoryTelemetryStorage; -import io.split.telemetry.storage.TelemetryStorage; -import io.split.telemetry.storage.TelemetryStorageProducer; -import io.split.telemetry.synchronizer.TelemetryInMemorySubmitter; -import io.split.telemetry.synchronizer.TelemetrySynchronizer; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -25,7 +19,6 @@ import org.mockito.Captor; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; -import pluggable.CustomStorageWrapper; import java.net.URISyntaxException; import java.util.*; @@ -43,11 +36,11 @@ */ @RunWith(MockitoJUnitRunner.class) public class ImpressionsManagerImplTest { - private static TelemetryStorage TELEMETRY_STORAGE = Mockito.mock(InMemoryTelemetryStorage.class); + private static ImpressionsTelemetryRecorder TELEMETRY_RECORDER = Mockito.mock(ImpressionsTelemetryRecorder.class); @Before public void setUp() { - TELEMETRY_STORAGE = Mockito.mock(InMemoryTelemetryStorage.class); + TELEMETRY_RECORDER = Mockito.mock(ImpressionsTelemetryRecorder.class); } @Captor @@ -64,12 +57,10 @@ public void setUp() { @Test public void works() throws URISyntaxException { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(4) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(4); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); @@ -78,7 +69,7 @@ public void works() throws URISyntaxException { ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, null, null); @@ -103,24 +94,21 @@ public void works() throws URISyntaxException { @Test public void testImpressionListenerOptimize() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.OPTIMIZED) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.OPTIMIZED) .build(); ImpressionsStorage storage = Mockito.mock(InMemoryImpressionsStorage.class); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); ImpressionObserver impressionObserver = new ImpressionObserver(200); - TelemetryStorageProducer telemetryStorageProducer = new InMemoryTelemetryStorage(); - - ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(true, impressionObserver, impressionCounter, telemetryStorageProducer); + + ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(true, impressionObserver, impressionCounter, TELEMETRY_RECORDER); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); ImpressionListener impressionListener = Mockito.mock(AsynchronousImpressionListener.class); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, impressionListener); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, impressionListener); treatmentLog.start(); KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, 1L, null); @@ -146,10 +134,8 @@ public void testImpressionListenerOptimize() { @Test public void testImpressionListenerDebug() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(6) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); ImpressionsStorage storage = Mockito.mock(InMemoryImpressionsStorage.class); @@ -162,7 +148,7 @@ public void testImpressionListenerDebug() { ImpressionListener impressionListener = Mockito.mock(AsynchronousImpressionListener.class); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, impressionListener); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, impressionListener); treatmentLog.start(); KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, 1L, null); @@ -188,17 +174,15 @@ public void testImpressionListenerDebug() { @Test public void testImpressionListenerNone() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.NONE) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.NONE) .build(); ImpressionsStorage storage = Mockito.mock(InMemoryImpressionsStorage.class); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); + UniqueKeysSender uniqueKeysSender = Mockito.mock(UniqueKeysSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); - UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null); + UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(uniqueKeysSender, 1000, 1000, null); uniqueKeysTracker.start(); ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionNone(true, uniqueKeysTracker, impressionCounter); @@ -206,7 +190,7 @@ public void testImpressionListenerNone() { ImpressionListener impressionListener = Mockito.mock(AsynchronousImpressionListener.class); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, impressionListener); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, impressionListener); treatmentLog.start(); KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, 1L, null); @@ -233,12 +217,10 @@ public void testImpressionListenerNone() { @Test public void worksButDropsImpressions() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(3) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(3); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); @@ -247,7 +229,7 @@ public void worksButDropsImpressions() { ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -269,18 +251,16 @@ public void worksButDropsImpressions() { List captured = impressionsCaptor.getValue(); Assert.assertEquals(3, captured.size()); - verify(TELEMETRY_STORAGE, times(1)).recordImpressionStats(ImpressionsDataTypeEnum.IMPRESSIONS_DROPPED, 1); + verify(TELEMETRY_RECORDER, times(1)).recordImpressionsDropped(1); } @Test public void works4ImpressionsInOneTest() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); @@ -289,7 +269,7 @@ public void works4ImpressionsInOneTest() { ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -313,18 +293,16 @@ public void works4ImpressionsInOneTest() { Assert.assertEquals(1, captured.size()); Assert.assertEquals(4, captured.get(0).keyImpressions.size()); Assert.assertEquals(ki1, captured.get(0).keyImpressions.get(0)); - verify(TELEMETRY_STORAGE, times(4)).recordImpressionStats(ImpressionsDataTypeEnum.IMPRESSIONS_QUEUED, 1); + verify(TELEMETRY_RECORDER, times(4)).recordImpressionsQueued(1); } @Test public void worksNoImpressions() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); @@ -333,7 +311,7 @@ public void worksNoImpressions() { ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); // There are no impressions to post. @@ -345,12 +323,10 @@ public void worksNoImpressions() { @Test public void alreadySeenImpressionsAreMarked() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); @@ -358,7 +334,7 @@ public void alreadySeenImpressionsAreMarked() { ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -402,22 +378,19 @@ public void alreadySeenImpressionsAreMarked() { @Test public void testImpressionsStandaloneModeOptimizedMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.OPTIMIZED) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.OPTIMIZED) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); ImpressionObserver impressionObserver = new ImpressionObserver(200); - TelemetryStorageProducer telemetryStorageProducer = new InMemoryTelemetryStorage(); - - ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, telemetryStorageProducer); + + ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, TELEMETRY_RECORDER); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -459,12 +432,10 @@ public void testImpressionsStandaloneModeOptimizedMode() { @Test public void testImpressionsStandaloneModeDebugMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); @@ -472,7 +443,7 @@ public void testImpressionsStandaloneModeDebugMode() { ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -508,23 +479,21 @@ public void testImpressionsStandaloneModeDebugMode() { @Test public void testImpressionsStandaloneModeNoneMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.NONE) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.NONE) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); + UniqueKeysSender uniqueKeysSender = Mockito.mock(UniqueKeysSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); - UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null); + UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(uniqueKeysSender, 1000, 1000, null); uniqueKeysTracker.start(); ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionNone(false, uniqueKeysTracker, impressionCounter); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -540,7 +509,7 @@ public void testImpressionsStandaloneModeNoneMode() { treatmentLog.close(); uniqueKeysTracker.stop(); - verify(telemetrySynchronizer).synchronizeUniqueKeys(uniqueKeysCaptor.capture()); + verify(uniqueKeysSender).send(uniqueKeysCaptor.capture()); List uniqueKeysList = uniqueKeysCaptor.getAllValues(); UniqueKeys uniqueKeys = uniqueKeysList.get(0); @@ -565,23 +534,18 @@ public void testImpressionsStandaloneModeNoneMode() { @Test public void testImpressionsConsumerModeOptimizedMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.OPTIMIZED) - .operationMode(OperationMode.CONSUMER) - .customStorageWrapper(Mockito.mock(CustomStorageWrapper.class)) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.OPTIMIZED) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); ImpressionObserver impressionObserver = new ImpressionObserver(200); - TelemetryStorageProducer telemetryStorageProducer = new InMemoryTelemetryStorage(); - - ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, telemetryStorageProducer); + + ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, TELEMETRY_RECORDER); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -623,24 +587,20 @@ public void testImpressionsConsumerModeOptimizedMode() { @Test public void testImpressionsConsumerModeNoneMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.NONE) - .operationMode(OperationMode.CONSUMER) - .customStorageWrapper(Mockito.mock(CustomStorageWrapper.class)) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.NONE) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); + UniqueKeysSender uniqueKeysSender = Mockito.mock(UniqueKeysSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); - UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null); + UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(uniqueKeysSender, 1000, 1000, null); uniqueKeysTracker.start(); ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionNone(false, uniqueKeysTracker, impressionCounter); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -656,7 +616,7 @@ public void testImpressionsConsumerModeNoneMode() { uniqueKeysTracker.stop(); treatmentLog.close(); - verify(telemetrySynchronizer).synchronizeUniqueKeys(uniqueKeysCaptor.capture()); + verify(uniqueKeysSender).send(uniqueKeysCaptor.capture()); List uniqueKeysList = uniqueKeysCaptor.getAllValues(); UniqueKeys uniqueKeys = uniqueKeysList.get(0); @@ -681,14 +641,10 @@ public void testImpressionsConsumerModeNoneMode() { @Test public void testImpressionsConsumerModeDebugMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) - .operationMode(OperationMode.CONSUMER) - .customStorageWrapper(Mockito.mock(CustomStorageWrapper.class)) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); @@ -696,7 +652,7 @@ public void testImpressionsConsumerModeDebugMode() { ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -732,142 +688,119 @@ public void testImpressionsConsumerModeDebugMode() { @Test public void testCounterStandaloneModeOptimizedMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.OPTIMIZED) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.OPTIMIZED) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); ImpressionObserver impressionObserver = new ImpressionObserver(200); - TelemetryStorageProducer telemetryStorageProducer = new InMemoryTelemetryStorage(); - - ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, telemetryStorageProducer); + + ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, TELEMETRY_RECORDER); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); manager.start(); Assert.assertNotNull(manager.getCounter()); } @Test public void testCounterStandaloneModeDebugMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionObserver impressionObserver = new ImpressionObserver(200); ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, null, null); + ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, null, null); manager.start(); Assert.assertNull(manager.getCounter()); } @Test public void testCounterStandaloneModeNoneMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.NONE) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.NONE) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ProcessImpressionStrategy processImpressionStrategy = Mockito.mock(ProcessImpressionNone.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); - ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, Mockito.mock(ProcessImpressionNone.class), processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, Mockito.mock(ProcessImpressionNone.class), processImpressionStrategy, impressionCounter, null); manager.start(); Assert.assertNotNull(manager.getCounter()); } @Test public void testCounterConsumerModeOptimizedMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.OPTIMIZED) - .operationMode(OperationMode.CONSUMER) - .customStorageWrapper(Mockito.mock(CustomStorageWrapper.class)) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.OPTIMIZED) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ProcessImpressionStrategy processImpressionStrategy = Mockito.mock(ProcessImpressionOptimized.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); - ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, Mockito.mock(ProcessImpressionNone.class), processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, Mockito.mock(ProcessImpressionNone.class), processImpressionStrategy, impressionCounter, null); manager.start(); Assert.assertNotNull(manager.getCounter()); } @Test public void testCounterConsumerModeDebugMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) - .operationMode(OperationMode.CONSUMER) - .customStorageWrapper(Mockito.mock(CustomStorageWrapper.class)) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ProcessImpressionStrategy processImpressionStrategy = Mockito.mock(ProcessImpressionDebug.class); - ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, Mockito.mock(ProcessImpressionNone.class), processImpressionStrategy, null, null); + ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, Mockito.mock(ProcessImpressionNone.class), processImpressionStrategy, null, null); manager.start(); Assert.assertNull(manager.getCounter()); } @Test public void testCounterConsumerModeNoneMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.NONE) - .operationMode(OperationMode.CONSUMER) - .customStorageWrapper(Mockito.mock(CustomStorageWrapper.class)) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.NONE) .build(); - - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ProcessImpressionStrategy processImpressionStrategy = Mockito.mock(ProcessImpressionNone.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); - ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, Mockito.mock(ProcessImpressionNone.class), processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl manager = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, Mockito.mock(ProcessImpressionNone.class), processImpressionStrategy, impressionCounter, null); manager.start(); Assert.assertNotNull(manager.getCounter()); } @Test public void testImpressionToggleStandaloneOptimizedMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.OPTIMIZED) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.OPTIMIZED) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); ImpressionObserver impressionObserver = new ImpressionObserver(200); - TelemetryStorageProducer telemetryStorageProducer = new InMemoryTelemetryStorage(); - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); - UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null); + UniqueKeysSender uniqueKeysSender1 = Mockito.mock(UniqueKeysSender.class); + UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(uniqueKeysSender1, 1000, 1000, null); uniqueKeysTracker.start(); - ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, telemetryStorageProducer); + ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, TELEMETRY_RECORDER); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, uniqueKeysTracker, impressionCounter); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -916,23 +849,21 @@ public void testImpressionToggleStandaloneOptimizedMode() { @Test public void testImpressionToggleStandaloneModeDebugMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); ImpressionObserver impressionObserver = new ImpressionObserver(200); ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); - UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null); + UniqueKeysSender uniqueKeysSender2 = Mockito.mock(UniqueKeysSender.class); + UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(uniqueKeysSender2, 1000, 1000, null); uniqueKeysTracker.start(); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, uniqueKeysTracker, impressionCounter); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -971,23 +902,21 @@ public void testImpressionToggleStandaloneModeDebugMode() { @Test public void testImpressionToggleStandaloneModeNoneMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.NONE) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.NONE) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); + UniqueKeysSender uniqueKeysSender = Mockito.mock(UniqueKeysSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); - UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null); + UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(uniqueKeysSender, 1000, 1000, null); uniqueKeysTracker.start(); ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionNone(false, uniqueKeysTracker, impressionCounter); ProcessImpressionNone processImpressionNone = (ProcessImpressionNone) processImpressionStrategy; - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -1026,23 +955,18 @@ public void testImpressionToggleStandaloneModeNoneMode() { @Test public void testImpressionsPropertiesOptimizedMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.OPTIMIZED) - .operationMode(OperationMode.CONSUMER) - .customStorageWrapper(Mockito.mock(CustomStorageWrapper.class)) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.OPTIMIZED) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = new ImpressionCounter(); ImpressionObserver impressionObserver = new ImpressionObserver(200); - TelemetryStorageProducer telemetryStorageProducer = new InMemoryTelemetryStorage(); - - ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, telemetryStorageProducer); + + ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, TELEMETRY_RECORDER); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. @@ -1085,14 +1009,10 @@ public void testImpressionsPropertiesOptimizedMode() { @Test public void testImpressionsPropertiesDebugMode() { - SplitClientConfig config = SplitClientConfig.builder() - .impressionsQueueSize(10) - .endpoint("nowhere.com", "nowhere.com") - .impressionsMode(ImpressionsManager.Mode.DEBUG) - .operationMode(OperationMode.CONSUMER) - .customStorageWrapper(Mockito.mock(CustomStorageWrapper.class)) + ImpressionsManagerConfig config = ImpressionsManagerConfig.builder() + .mode(ImpressionsManager.Mode.DEBUG) .build(); - ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize()); + ImpressionsStorage storage = new InMemoryImpressionsStorage(10); ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class); ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class); @@ -1100,7 +1020,7 @@ public void testImpressionsPropertiesDebugMode() { ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, null, null); - ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); + ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_RECORDER, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null); treatmentLog.start(); // These 4 unique test name will cause 4 entries but we are caping at the first 3. From 002a5937fdc20b8aeb93fa8481101029366a25d7 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Tue, 28 Apr 2026 14:56:28 -0300 Subject: [PATCH 6/6] FME-15373-impressions: wire client/ back to impressions module Add impressions dependency to client/pom.xml. Add adapter classes TelemetryRuntimeImpressionsRecorder and TelemetryUniqueKeysSender to bridge the old telemetry interfaces to the new impressions module boundaries. Update SplitFactoryImpl to build ImpressionsManagerConfig and use the adapters when constructing ImpressionsManagerImpl and UniqueKeysTrackerImp. Move strategy tests and UniqueKeysTrackerImpTest to the impressions module where their dependencies now live. AI-Session-Id: 52375eb8-af89-45b8-bbad-1698b6636202 AI-Tool: claude-code AI-Model: unknown --- client/pom.xml | 5 + .../io/split/client/SplitFactoryImpl.java | 17 +- .../TelemetryRuntimeImpressionsRecorder.java | 23 +++ .../TelemetryUniqueKeysSender.java | 18 ++ .../impressions/UniqueKeysTrackerImpTest.java | 178 ------------------ .../strategy/ProcessImpressionDebugTest.java | 16 +- .../strategy/ProcessImpressionNoneTest.java | 33 ++-- .../ProcessImpressionOptimizedTest.java | 28 +-- 8 files changed, 95 insertions(+), 223 deletions(-) create mode 100644 client/src/main/java/io/split/client/impressions/TelemetryRuntimeImpressionsRecorder.java create mode 100644 client/src/main/java/io/split/client/impressions/TelemetryUniqueKeysSender.java delete mode 100644 client/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java rename {client => impressions}/src/test/java/io/split/client/impressions/strategy/ProcessImpressionDebugTest.java (82%) rename {client => impressions}/src/test/java/io/split/client/impressions/strategy/ProcessImpressionNoneTest.java (74%) rename {client => impressions}/src/test/java/io/split/client/impressions/strategy/ProcessImpressionOptimizedTest.java (77%) diff --git a/client/pom.xml b/client/pom.xml index 6d5bab62..5c13cc44 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -172,6 +172,11 @@ tracker ${project.version}
+ + io.split.client + impressions + ${project.version} + io.split.client targeting-engine diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java index 05f21d85..41a4364f 100644 --- a/client/src/main/java/io/split/client/SplitFactoryImpl.java +++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java @@ -17,8 +17,12 @@ import io.split.client.impressions.ImpressionListener; import io.split.client.impressions.ImpressionObserver; import io.split.client.impressions.ImpressionsManager; +import io.split.client.impressions.ImpressionsManagerConfig; import io.split.client.impressions.ImpressionsManagerImpl; +import io.split.client.impressions.ImpressionsTelemetryRecorder; import io.split.client.impressions.ImpressionsSender; +import io.split.client.impressions.TelemetryRuntimeImpressionsRecorder; +import io.split.client.impressions.TelemetryUniqueKeysSender; import io.split.client.impressions.ImpressionsStorage; import io.split.client.impressions.ImpressionsStorageConsumer; import io.split.client.impressions.ImpressionsStorageProducer; @@ -752,11 +756,12 @@ private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config, : null; ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(listener != null, _uniqueKeysTracker, counter); + ImpressionsTelemetryRecorder telemetryRecorder = new TelemetryRuntimeImpressionsRecorder(_telemetryStorageProducer); switch (config.impressionsMode()) { case OPTIMIZED: ImpressionObserver impressionObserver = new ImpressionObserver(config.getLastSeenCacheSize()); processImpressionStrategy = new ProcessImpressionOptimized(listener != null, impressionObserver, - counter, _telemetryStorageProducer); + counter, telemetryRecorder); break; case DEBUG: impressionObserver = new ImpressionObserver(config.getLastSeenCacheSize()); @@ -766,7 +771,13 @@ private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config, processImpressionStrategy = processImpressionNone; break; } - return ImpressionsManagerImpl.instance(config, _telemetryStorageProducer, impressionsStorageConsumer, + ImpressionsManagerConfig impressionsManagerConfig = ImpressionsManagerConfig.builder() + .mode(config.impressionsMode()) + .impressionsRefreshRateSeconds(config.impressionsRefreshRate()) + .threadFactory(config.getThreadFactory()) + .debugEnabled(config.debugEnabled()) + .build(); + return ImpressionsManagerImpl.instance(impressionsManagerConfig, telemetryRecorder, impressionsStorageConsumer, impressionsStorageProducer, _impressionsSender, processImpressionNone, processImpressionStrategy, counter, listener); } @@ -809,7 +820,7 @@ private UniqueKeysTracker createUniqueKeysTracker(SplitClientConfig config) { int uniqueKeysRefreshRate = config.operationMode().equals(OperationMode.STANDALONE) ? config.uniqueKeysRefreshRateInMemory() : config.uniqueKeysRefreshRateRedis(); - return new UniqueKeysTrackerImp(_telemetrySynchronizer, uniqueKeysRefreshRate, + return new UniqueKeysTrackerImp(new TelemetryUniqueKeysSender(_telemetrySynchronizer), uniqueKeysRefreshRate, config.filterUniqueKeysRefreshRate(), config.getThreadFactory()); } diff --git a/client/src/main/java/io/split/client/impressions/TelemetryRuntimeImpressionsRecorder.java b/client/src/main/java/io/split/client/impressions/TelemetryRuntimeImpressionsRecorder.java new file mode 100644 index 00000000..02b28701 --- /dev/null +++ b/client/src/main/java/io/split/client/impressions/TelemetryRuntimeImpressionsRecorder.java @@ -0,0 +1,23 @@ +package io.split.client.impressions; + +import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum; +import io.split.telemetry.storage.TelemetryRuntimeProducer; + +public class TelemetryRuntimeImpressionsRecorder implements ImpressionsTelemetryRecorder { + + private final TelemetryRuntimeProducer _telemetryRuntimeProducer; + + public TelemetryRuntimeImpressionsRecorder(TelemetryRuntimeProducer telemetryRuntimeProducer) { + _telemetryRuntimeProducer = telemetryRuntimeProducer; + } + + @Override + public void recordImpressionsDropped(long count) { + _telemetryRuntimeProducer.recordImpressionStats(ImpressionsDataTypeEnum.IMPRESSIONS_DROPPED, count); + } + + @Override + public void recordImpressionsQueued(long count) { + _telemetryRuntimeProducer.recordImpressionStats(ImpressionsDataTypeEnum.IMPRESSIONS_QUEUED, count); + } +} diff --git a/client/src/main/java/io/split/client/impressions/TelemetryUniqueKeysSender.java b/client/src/main/java/io/split/client/impressions/TelemetryUniqueKeysSender.java new file mode 100644 index 00000000..faae8792 --- /dev/null +++ b/client/src/main/java/io/split/client/impressions/TelemetryUniqueKeysSender.java @@ -0,0 +1,18 @@ +package io.split.client.impressions; + +import io.split.client.dtos.UniqueKeys; +import io.split.telemetry.synchronizer.TelemetrySynchronizer; + +public class TelemetryUniqueKeysSender implements UniqueKeysSender { + + private final TelemetrySynchronizer _telemetrySynchronizer; + + public TelemetryUniqueKeysSender(TelemetrySynchronizer telemetrySynchronizer) { + _telemetrySynchronizer = telemetrySynchronizer; + } + + @Override + public void send(UniqueKeys uniqueKeys) { + _telemetrySynchronizer.synchronizeUniqueKeys(uniqueKeys); + } +} diff --git a/client/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java b/client/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java deleted file mode 100644 index e758369e..00000000 --- a/client/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java +++ /dev/null @@ -1,178 +0,0 @@ -package io.split.client.impressions; - -import io.split.client.dtos.UniqueKeys; -import io.split.telemetry.synchronizer.TelemetryInMemorySubmitter; -import io.split.telemetry.synchronizer.TelemetrySynchronizer; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; - -public class UniqueKeysTrackerImpTest { - private static TelemetrySynchronizer _telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); - - @Test - public void addSomeElements(){ - UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(_telemetrySynchronizer, 10000, 10000, null); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key3")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key4")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key5")); - - HashMap> result = uniqueKeysTrackerImp.popAll(); - Assert.assertEquals(2,result.size()); - - HashSet value1 = result.get("feature1"); - Assert.assertEquals(3,value1.size()); - Assert.assertTrue(value1.contains("key1")); - Assert.assertTrue(value1.contains("key2")); - Assert.assertTrue(value1.contains("key3")); - - HashSet value2 = result.get("feature2"); - Assert.assertEquals(2,value2.size()); - Assert.assertTrue(value2.contains("key4")); - Assert.assertTrue(value2.contains("key5")); - } - - @Test - public void addTheSameElements(){ - UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(_telemetrySynchronizer, 10000, 10000, null); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key3")); - - Assert.assertFalse(uniqueKeysTrackerImp.track("feature1","key1")); - Assert.assertFalse(uniqueKeysTrackerImp.track("feature1","key2")); - Assert.assertFalse(uniqueKeysTrackerImp.track("feature1","key3")); - - HashMap> result = uniqueKeysTrackerImp.popAll(); - Assert.assertEquals(1,result.size()); - - HashSet value1 = result.get("feature1"); - Assert.assertEquals(3,value1.size()); - Assert.assertTrue(value1.contains("key1")); - Assert.assertTrue(value1.contains("key2")); - Assert.assertTrue(value1.contains("key3")); - } - - @Test - public void popAllUniqueKeys(){ - UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(_telemetrySynchronizer, 10000, 10000, null); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key3")); - - HashMap> result = uniqueKeysTrackerImp.popAll(); - Assert.assertEquals(2,result.size()); - HashMap> resultAfterPopAll = uniqueKeysTrackerImp.popAll(); - Assert.assertEquals(0,resultAfterPopAll.size()); - } - - @Test - public void testSynchronization() throws Exception { - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); - UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(telemetrySynchronizer, 1, 3, null); - uniqueKeysTrackerImp.start(); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key3")); - - Thread.sleep(2900); - Mockito.verify(telemetrySynchronizer, Mockito.times(1)).synchronizeUniqueKeys(Mockito.anyObject()); - Thread.sleep(2900); - Mockito.verify(telemetrySynchronizer, Mockito.times(1)).synchronizeUniqueKeys(Mockito.anyObject()); - } - - @Test - public void testStopSynchronization() throws Exception { - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); - UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(telemetrySynchronizer, 1, 2, null); - uniqueKeysTrackerImp.start(); - Assert.assertFalse(uniqueKeysTrackerImp.getSendGuard().get()); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2")); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key3")); - - Thread.sleep(2100); - Mockito.verify(telemetrySynchronizer, Mockito.times(1)).synchronizeUniqueKeys(Mockito.anyObject()); - uniqueKeysTrackerImp.stop(); - Mockito.verify(telemetrySynchronizer, Mockito.times(1)).synchronizeUniqueKeys(Mockito.anyObject()); - } - - @Test - public void testUniqueKeysChunks() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(_telemetrySynchronizer, 10000, 10000, null); - HashMap> uniqueKeysHashMap = new HashMap<>(); - HashSet feature1 = new HashSet<>(); - HashSet feature2 = new HashSet<>(); - HashSet feature3 = new HashSet<>(); - HashSet feature4 = new HashSet<>(); - HashSet feature5 = new HashSet<>(); - for (Integer i=1; i<6000; i++) { - if (i <= 1000) { - feature1.add("key" + i); - } - if (i <= 2000) { - feature2.add("key" + i); - } - if (i <= 3000) { - feature3.add("key" + i); - } - if (i <= 4000) { - feature4.add("key" + i); - } - feature5.add("key" + i); - } - uniqueKeysHashMap.put("feature1", feature1); - uniqueKeysHashMap.put("feature2", feature2); - uniqueKeysHashMap.put("feature3", feature3); - uniqueKeysHashMap.put("feature4", feature4); - uniqueKeysHashMap.put("feature5", feature5); - - List uniqueKeysFromPopAll = new ArrayList<>(); - for (Map.Entry> uniqueKeyEntry : uniqueKeysHashMap.entrySet()) { - UniqueKeys.UniqueKey uniqueKey = new UniqueKeys.UniqueKey(uniqueKeyEntry.getKey(), new ArrayList<>(uniqueKeyEntry.getValue())); - uniqueKeysFromPopAll.add(uniqueKey); - } - Method methodCapChunks = uniqueKeysTrackerImp.getClass().getDeclaredMethod("capChunksToMaxSize", List.class); - methodCapChunks.setAccessible(true); - uniqueKeysFromPopAll = (List)methodCapChunks.invoke(uniqueKeysTrackerImp, uniqueKeysFromPopAll); - - Method methodGetChunks = uniqueKeysTrackerImp.getClass().getDeclaredMethod("getChunks", List.class); - methodGetChunks.setAccessible(true); - List> keysChunks = (List>) methodGetChunks.invoke(uniqueKeysTrackerImp, uniqueKeysFromPopAll); - for (List chunk : keysChunks) { - int chunkSize = 0; - for (UniqueKeys.UniqueKey keys : chunk) { - chunkSize += keys.keysDto.size(); - } - Assert.assertTrue(chunkSize <= 5000); - } - } - - @Test - public void testTrackReachMaxKeys() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); - UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(telemetrySynchronizer, 10000, 10000, null); - for (int i=1; i<6000; i++) { - Assert.assertTrue(uniqueKeysTrackerImp.track("feature1", "key" + i)); - Assert.assertTrue(uniqueKeysTrackerImp.track("feature2", "key" + i)); - } - Mockito.verify(telemetrySynchronizer, Mockito.times(2)).synchronizeUniqueKeys(Mockito.anyObject()); - - Field getTrackerSize = uniqueKeysTrackerImp.getClass().getDeclaredField("trackerKeysSize"); - getTrackerSize.setAccessible(true); - AtomicInteger trackerSize = (AtomicInteger) getTrackerSize.get(uniqueKeysTrackerImp); - Assert.assertTrue(trackerSize.intValue() == 1998); - } -} \ No newline at end of file diff --git a/client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionDebugTest.java b/impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionDebugTest.java similarity index 82% rename from client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionDebugTest.java rename to impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionDebugTest.java index 68be97c5..e464745c 100644 --- a/client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionDebugTest.java +++ b/impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionDebugTest.java @@ -6,11 +6,8 @@ import io.split.client.impressions.Impression; import io.split.client.impressions.ImpressionObserver; import io.split.client.impressions.ImpressionsResult; -import io.split.telemetry.storage.InMemoryTelemetryStorage; -import io.split.telemetry.storage.TelemetryStorage; import org.junit.Assert; import org.junit.Test; -import org.mockito.Mockito; import java.util.ArrayList; import java.util.List; @@ -18,10 +15,9 @@ public class ProcessImpressionDebugTest { private static final long LAST_SEEN_CACHE_SIZE = 500000; - private static TelemetryStorage TELEMETRY_STORAGE = Mockito.mock(InMemoryTelemetryStorage.class); @Test - public void processImpressionsWithListener(){ + public void processImpressionsWithListener() { boolean listenerEnable = true; ImpressionObserver impressionObserver = new ImpressionObserver(LAST_SEEN_CACHE_SIZE); ProcessImpressionDebug processImpressionDebug = new ProcessImpressionDebug(listenerEnable, impressionObserver); @@ -40,12 +36,12 @@ public void processImpressionsWithListener(){ long pt3 = impressionsResult1.getImpressionsToQueue().get(2).pt(); Assert.assertEquals(1, pt3); - Assert.assertEquals(3,impressionsResult1.getImpressionsToQueue().size()); - Assert.assertEquals(3,impressionsResult1.getImpressionsToListener().size()); + Assert.assertEquals(3, impressionsResult1.getImpressionsToQueue().size()); + Assert.assertEquals(3, impressionsResult1.getImpressionsToListener().size()); } @Test - public void processImpressionsWithoutListener(){ + public void processImpressionsWithoutListener() { boolean listenerEnable = false; ImpressionObserver impressionObserver = new ImpressionObserver(LAST_SEEN_CACHE_SIZE); ProcessImpressionDebug processImpressionDebug = new ProcessImpressionDebug(listenerEnable, impressionObserver); @@ -64,7 +60,7 @@ public void processImpressionsWithoutListener(){ long pt3 = impressionsResult1.getImpressionsToQueue().get(2).pt(); Assert.assertEquals(1, pt3); - Assert.assertEquals(3,impressionsResult1.getImpressionsToQueue().size()); + Assert.assertEquals(3, impressionsResult1.getImpressionsToQueue().size()); Assert.assertNull(impressionsResult1.getImpressionsToListener()); } -} \ No newline at end of file +} diff --git a/client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionNoneTest.java b/impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionNoneTest.java similarity index 74% rename from client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionNoneTest.java rename to impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionNoneTest.java index 1debedd1..51f8ede9 100644 --- a/client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionNoneTest.java +++ b/impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionNoneTest.java @@ -2,31 +2,28 @@ import io.split.client.dtos.KeyImpression; import io.split.client.impressions.Impression; +import io.split.client.impressions.ImpressionCounter; import io.split.client.impressions.ImpressionsResult; +import io.split.client.impressions.UniqueKeysSender; import io.split.client.impressions.UniqueKeysTrackerImp; -import io.split.client.impressions.ImpressionCounter; -import io.split.telemetry.storage.InMemoryTelemetryStorage; -import io.split.telemetry.storage.TelemetryStorage; -import io.split.telemetry.synchronizer.TelemetryInMemorySubmitter; -import io.split.telemetry.synchronizer.TelemetrySynchronizer; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import static io.split.client.impressions.ImpressionTestUtils.keyImpression; public class ProcessImpressionNoneTest { - private static TelemetryStorage TELEMETRY_STORAGE = Mockito.mock(InMemoryTelemetryStorage.class); - @Test - public void processImpressionsWithListener(){ + public void processImpressionsWithListener() { boolean listenerEnable = true; ImpressionCounter counter = new ImpressionCounter(); - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); - UniqueKeysTrackerImp uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 10000, 10000, null); + UniqueKeysSender sender = Mockito.mock(UniqueKeysSender.class); + UniqueKeysTrackerImp uniqueKeysTracker = new UniqueKeysTrackerImp(sender, 10000, 10000, null); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(listenerEnable, uniqueKeysTracker, counter); KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, null, null); @@ -39,8 +36,8 @@ public void processImpressionsWithListener(){ impressions.add(new Impression(ki3.keyName, null, ki3.feature, ki3.treatment, ki3.time, null, 1L, null, null)); ImpressionsResult impressionsResult1 = processImpressionNone.process(impressions); - Assert.assertEquals(0,impressionsResult1.getImpressionsToQueue().size()); - Assert.assertEquals(3,impressionsResult1.getImpressionsToListener().size()); + Assert.assertEquals(0, impressionsResult1.getImpressionsToQueue().size()); + Assert.assertEquals(3, impressionsResult1.getImpressionsToListener().size()); Assert.assertEquals(2, uniqueKeysTracker.popAll().size()); HashMap counters = counter.popAll(); @@ -48,11 +45,11 @@ public void processImpressionsWithListener(){ } @Test - public void processImpressionsWithoutListener(){ + public void processImpressionsWithoutListener() { boolean listenerEnable = false; ImpressionCounter counter = new ImpressionCounter(); - TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class); - UniqueKeysTrackerImp uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 10000, 10000, null); + UniqueKeysSender sender = Mockito.mock(UniqueKeysSender.class); + UniqueKeysTrackerImp uniqueKeysTracker = new UniqueKeysTrackerImp(sender, 10000, 10000, null); ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(listenerEnable, uniqueKeysTracker, counter); KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, null, null); @@ -65,9 +62,9 @@ public void processImpressionsWithoutListener(){ impressions.add(new Impression(ki3.keyName, null, ki3.feature, ki3.treatment, ki3.time, null, 1L, null, null)); ImpressionsResult impressionsResult1 = processImpressionNone.process(impressions); - Assert.assertEquals(0,impressionsResult1.getImpressionsToQueue().size()); + Assert.assertEquals(0, impressionsResult1.getImpressionsToQueue().size()); Assert.assertNull(impressionsResult1.getImpressionsToListener()); Assert.assertEquals(2, uniqueKeysTracker.popAll().size()); Assert.assertEquals(2, counter.popAll().size()); } -} \ No newline at end of file +} diff --git a/client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionOptimizedTest.java b/impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionOptimizedTest.java similarity index 77% rename from client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionOptimizedTest.java rename to impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionOptimizedTest.java index f7cecebe..e7ec04ba 100644 --- a/client/src/test/java/io/split/client/impressions/strategy/ProcessImpressionOptimizedTest.java +++ b/impressions/src/test/java/io/split/client/impressions/strategy/ProcessImpressionOptimizedTest.java @@ -3,16 +3,14 @@ import static io.split.client.impressions.ImpressionTestUtils.keyImpression; import io.split.client.dtos.KeyImpression; - import io.split.client.impressions.Impression; +import io.split.client.impressions.ImpressionCounter; import io.split.client.impressions.ImpressionObserver; import io.split.client.impressions.ImpressionsResult; -import io.split.client.impressions.ImpressionCounter; -import io.split.telemetry.storage.InMemoryTelemetryStorage; -import io.split.telemetry.storage.TelemetryStorage; +import io.split.client.impressions.ImpressionsTelemetryRecorder; +import io.split.client.impressions.NoopImpressionsTelemetryRecorder; import org.junit.Assert; import org.junit.Test; -import org.mockito.Mockito; import java.util.ArrayList; import java.util.List; @@ -20,14 +18,15 @@ public class ProcessImpressionOptimizedTest { private static final long LAST_SEEN_CACHE_SIZE = 500000; - private static TelemetryStorage TELEMETRY_STORAGE = Mockito.mock(InMemoryTelemetryStorage.class); + private static final ImpressionsTelemetryRecorder NOOP_RECORDER = new NoopImpressionsTelemetryRecorder(); @Test - public void processImpressionsWithListener(){ + public void processImpressionsWithListener() { boolean listenerEnable = true; ImpressionObserver impressionObserver = new ImpressionObserver(LAST_SEEN_CACHE_SIZE); ImpressionCounter counter = new ImpressionCounter(); - ProcessImpressionOptimized processImpressionOptimized = new ProcessImpressionOptimized(listenerEnable, impressionObserver, counter, TELEMETRY_STORAGE); + ProcessImpressionOptimized processImpressionOptimized = new ProcessImpressionOptimized(listenerEnable, + impressionObserver, counter, NOOP_RECORDER); KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, null, null); KeyImpression ki2 = keyImpression("test2", "adil", "on", 1L, null, null); @@ -40,17 +39,18 @@ public void processImpressionsWithListener(){ ImpressionsResult impressionsResult1 = processImpressionOptimized.process(impressions); - Assert.assertEquals(2,impressionsResult1.getImpressionsToQueue().size()); - Assert.assertEquals(3,impressionsResult1.getImpressionsToListener().size()); + Assert.assertEquals(2, impressionsResult1.getImpressionsToQueue().size()); + Assert.assertEquals(3, impressionsResult1.getImpressionsToListener().size()); Assert.assertEquals(1, counter.popAll().size()); } @Test - public void processImpressionsWithoutListener(){ + public void processImpressionsWithoutListener() { boolean listenerEnable = false; ImpressionObserver impressionObserver = new ImpressionObserver(LAST_SEEN_CACHE_SIZE); ImpressionCounter counter = new ImpressionCounter(); - ProcessImpressionOptimized processImpressionOptimized = new ProcessImpressionOptimized(listenerEnable, impressionObserver, counter, TELEMETRY_STORAGE); + ProcessImpressionOptimized processImpressionOptimized = new ProcessImpressionOptimized(listenerEnable, + impressionObserver, counter, NOOP_RECORDER); KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, null, null); KeyImpression ki2 = keyImpression("test2", "adil", "on", 1L, null, null); @@ -62,8 +62,8 @@ public void processImpressionsWithoutListener(){ impressions.add(new Impression(ki3.keyName, null, ki3.feature, ki3.treatment, ki3.time, null, 1L, null, null)); ImpressionsResult impressionsResult1 = processImpressionOptimized.process(impressions); - Assert.assertEquals(2,impressionsResult1.getImpressionsToQueue().size()); + Assert.assertEquals(2, impressionsResult1.getImpressionsToQueue().size()); Assert.assertNull(impressionsResult1.getImpressionsToListener()); Assert.assertEquals(1, counter.popAll().size()); } -} \ No newline at end of file +}