|
1 | 1 | package io.split.client.impressions; |
2 | 2 |
|
| 3 | +import com.google.common.collect.Lists; |
3 | 4 | import io.split.client.dtos.UniqueKeys; |
4 | 5 | import io.split.client.impressions.filters.BloomFilterImp; |
5 | 6 | import io.split.client.impressions.filters.Filter; |
|
25 | 26 | public class UniqueKeysTrackerImp implements UniqueKeysTracker{ |
26 | 27 | private static final Logger _log = LoggerFactory.getLogger(UniqueKeysTrackerImp.class); |
27 | 28 | private static final double MARGIN_ERROR = 0.01; |
28 | | - private static final int MAX_AMOUNT_OF_TRACKED_UNIQUE_KEYS = 30000; |
| 29 | + private static final int MAX_UNIQUE_KEYS_POST_SIZE = 5000; |
29 | 30 | private static final int MAX_AMOUNT_OF_KEYS = 10000000; |
30 | 31 | private FilterAdapter filterAdapter; |
31 | 32 | private final TelemetrySynchronizer _telemetrySynchronizer; |
@@ -62,7 +63,7 @@ public boolean track(String featureFlagName, String key) { |
62 | 63 | return keysByFeature; |
63 | 64 | }); |
64 | 65 | _logger.debug("The feature flag " + featureFlagName + " and key " + key + " was added"); |
65 | | - if (uniqueKeysTracker.size() >= MAX_AMOUNT_OF_TRACKED_UNIQUE_KEYS){ |
| 66 | + if (getTrackerKeysSize() >= MAX_UNIQUE_KEYS_POST_SIZE){ |
66 | 67 | _logger.warn("The UniqueKeysTracker size reached the maximum limit"); |
67 | 68 | try { |
68 | 69 | sendUniqueKeys(); |
@@ -115,23 +116,75 @@ private void sendUniqueKeys(){ |
115 | 116 | _log.debug("SendUniqueKeys already running"); |
116 | 117 | return; |
117 | 118 | } |
| 119 | + |
118 | 120 | try { |
119 | 121 | if (uniqueKeysTracker.size() == 0) { |
120 | 122 | _log.debug("The Unique Keys Tracker is empty"); |
121 | 123 | return; |
122 | 124 | } |
| 125 | + |
123 | 126 | HashMap<String, HashSet<String>> uniqueKeysHashMap = popAll(); |
124 | 127 | List<UniqueKeys.UniqueKey> uniqueKeysFromPopAll = new ArrayList<>(); |
125 | 128 | for (Map.Entry<String, HashSet<String>> uniqueKeyEntry : uniqueKeysHashMap.entrySet()) { |
126 | 129 | UniqueKeys.UniqueKey uniqueKey = new UniqueKeys.UniqueKey(uniqueKeyEntry.getKey(), new ArrayList<>(uniqueKeyEntry.getValue())); |
127 | 130 | uniqueKeysFromPopAll.add(uniqueKey); |
128 | 131 | } |
129 | | - _telemetrySynchronizer.synchronizeUniqueKeys(new UniqueKeys(uniqueKeysFromPopAll)); |
| 132 | + uniqueKeysFromPopAll = capChunksToMaxSize(uniqueKeysFromPopAll); |
| 133 | + |
| 134 | + for (List<UniqueKeys.UniqueKey> chunk : getChunks(uniqueKeysFromPopAll)) { |
| 135 | + _telemetrySynchronizer.synchronizeUniqueKeys(new UniqueKeys(chunk)); |
| 136 | + } |
130 | 137 | } finally { |
131 | 138 | sendGuard.set(false); |
132 | 139 | } |
133 | 140 | } |
134 | 141 |
|
| 142 | + private List<UniqueKeys.UniqueKey> capChunksToMaxSize(List<UniqueKeys.UniqueKey> uniqeKeys) { |
| 143 | + List<UniqueKeys.UniqueKey> finalChunk = new ArrayList<>(); |
| 144 | + for (UniqueKeys.UniqueKey uniqueKey : uniqeKeys) { |
| 145 | + if (uniqueKey.keysDto.size() > MAX_UNIQUE_KEYS_POST_SIZE) { |
| 146 | + for(List<String> subChunk : Lists.partition(uniqueKey.keysDto, MAX_UNIQUE_KEYS_POST_SIZE)) { |
| 147 | + finalChunk.add(new UniqueKeys.UniqueKey(uniqueKey.featureName, subChunk)); |
| 148 | + } |
| 149 | + continue; |
| 150 | + } |
| 151 | + finalChunk.add(uniqueKey); |
| 152 | + } |
| 153 | + return finalChunk; |
| 154 | + } |
| 155 | + |
| 156 | + private List<List<UniqueKeys.UniqueKey>> getChunks(List<UniqueKeys.UniqueKey> uniqeKeys) { |
| 157 | + List<List<UniqueKeys.UniqueKey>> chunks = new ArrayList<>(); |
| 158 | + List<UniqueKeys.UniqueKey> intermediateChunk = new ArrayList<>(); |
| 159 | + for (UniqueKeys.UniqueKey uniqueKey : uniqeKeys) { |
| 160 | + if ((getChunkSize(intermediateChunk) + uniqueKey.keysDto.size()) > MAX_UNIQUE_KEYS_POST_SIZE) { |
| 161 | + chunks.add(intermediateChunk); |
| 162 | + intermediateChunk = new ArrayList<>(); |
| 163 | + } |
| 164 | + intermediateChunk.add(uniqueKey); |
| 165 | + } |
| 166 | + if (!intermediateChunk.isEmpty()) { |
| 167 | + chunks.add(intermediateChunk); |
| 168 | + } |
| 169 | + return chunks; |
| 170 | + } |
| 171 | + |
| 172 | + private int getChunkSize(List<UniqueKeys.UniqueKey> uniqueKeysChunk) { |
| 173 | + int totalSize = 0; |
| 174 | + for (UniqueKeys.UniqueKey uniqueKey : uniqueKeysChunk) { |
| 175 | + totalSize += uniqueKey.keysDto.size(); |
| 176 | + } |
| 177 | + return totalSize; |
| 178 | + } |
| 179 | + |
| 180 | + private int getTrackerKeysSize() { |
| 181 | + int totalSize = 0; |
| 182 | + for (String key : uniqueKeysTracker.keySet()) { |
| 183 | + totalSize += uniqueKeysTracker.get(key).size(); |
| 184 | + } |
| 185 | + return totalSize; |
| 186 | + } |
| 187 | + |
135 | 188 | private interface ExecuteUniqueKeysAction{ |
136 | 189 | void execute(); |
137 | 190 | } |
|
0 commit comments