Skip to content

Commit 7e192fb

Browse files
authored
Merge pull request #475 from splitio/SDKS-8067
[SDKS-8067] Fix deadlock when calling sendUniqueKeys
2 parents ba6f5ca + 0d5267e commit 7e192fb

File tree

11 files changed

+48
-28
lines changed

11 files changed

+48
-28
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
4.11.1 (Feb 29, 2024)
2+
- Fixed deadlock in UniqueKeysTracker when sending Unique Keys.
3+
14
4.11.0 (Jan 9, 2024)
25
- Added impressionsListener method in the IntegrationConfig builder to set Sync or Async Listener execution.
36
- Fixed localhost to read files with yml ending.

client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>io.split.client</groupId>
77
<artifactId>java-client-parent</artifactId>
8-
<version>4.11.0</version>
8+
<version>4.11.1</version>
99
</parent>
1010
<artifactId>java-client</artifactId>
1111
<packaging>jar</packaging>

client/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
import java.util.HashMap;
1515
import java.util.HashSet;
1616
import java.util.List;
17+
import java.util.Map;
18+
import java.util.Optional;
1719
import java.util.concurrent.ConcurrentHashMap;
1820
import java.util.concurrent.ScheduledExecutorService;
1921
import java.util.concurrent.ThreadFactory;
2022
import java.util.concurrent.TimeUnit;
23+
import java.util.concurrent.atomic.AtomicBoolean;
2124

2225
public class UniqueKeysTrackerImp implements UniqueKeysTracker{
2326
private static final Logger _log = LoggerFactory.getLogger(UniqueKeysTrackerImp.class);
@@ -31,6 +34,7 @@ public class UniqueKeysTrackerImp implements UniqueKeysTracker{
3134
private final ConcurrentHashMap<String,HashSet<String>> uniqueKeysTracker;
3235
private final int _uniqueKeysRefreshRate;
3336
private final int _filterRefreshRate;
37+
private final AtomicBoolean sendGuard = new AtomicBoolean(false);
3438
private static final Logger _logger = LoggerFactory.getLogger(UniqueKeysTrackerImp.class);
3539

3640
public UniqueKeysTrackerImp(TelemetrySynchronizer telemetrySynchronizer, int uniqueKeysRefreshRate, int filterRefreshRate,
@@ -46,19 +50,19 @@ public UniqueKeysTrackerImp(TelemetrySynchronizer telemetrySynchronizer, int uni
4650
}
4751

4852
@Override
49-
public synchronized boolean track(String featureFlagName, String key) {
53+
public boolean track(String featureFlagName, String key) {
5054
if (!filterAdapter.add(featureFlagName, key)) {
5155
_logger.debug("The feature flag " + featureFlagName + " and key " + key + " exist in the UniqueKeysTracker");
5256
return false;
5357
}
54-
HashSet<String> value = new HashSet<>();
55-
if(uniqueKeysTracker.containsKey(featureFlagName)){
56-
value = uniqueKeysTracker.get(featureFlagName);
57-
}
58-
value.add(key);
59-
uniqueKeysTracker.put(featureFlagName, value);
58+
uniqueKeysTracker.compute(featureFlagName,
59+
(feature, current) -> {
60+
HashSet<String> keysByFeature = Optional.ofNullable(current).orElse(new HashSet<>());
61+
keysByFeature.add(key);
62+
return keysByFeature;
63+
});
6064
_logger.debug("The feature flag " + featureFlagName + " and key " + key + " was added");
61-
if (uniqueKeysTracker.size() == MAX_AMOUNT_OF_TRACKED_UNIQUE_KEYS){
65+
if (uniqueKeysTracker.size() >= MAX_AMOUNT_OF_TRACKED_UNIQUE_KEYS){
6266
_logger.warn("The UniqueKeysTracker size reached the maximum limit");
6367
try {
6468
sendUniqueKeys();
@@ -107,17 +111,25 @@ public HashMap<String,HashSet<String>> popAll(){
107111
}
108112

109113
private void sendUniqueKeys(){
110-
if (uniqueKeysTracker.size() == 0) {
111-
_log.warn("The Unique Keys Tracker is empty");
112-
return;
114+
if (!sendGuard.compareAndSet(false, true)) {
115+
_log.debug("SendUniqueKeys already running");
116+
return;
113117
}
114-
HashMap<String, HashSet<String>> uniqueKeysHashMap = popAll();
115-
List<UniqueKeys.UniqueKey> uniqueKeysFromPopAll = new ArrayList<>();
116-
for (String featureFlag : uniqueKeysHashMap.keySet()) {
117-
UniqueKeys.UniqueKey uniqueKey = new UniqueKeys.UniqueKey(featureFlag, new ArrayList<>(uniqueKeysHashMap.get(featureFlag)));
118-
uniqueKeysFromPopAll.add(uniqueKey);
118+
try {
119+
if (uniqueKeysTracker.size() == 0) {
120+
_log.warn("The Unique Keys Tracker is empty");
121+
return;
122+
}
123+
HashMap<String, HashSet<String>> uniqueKeysHashMap = popAll();
124+
List<UniqueKeys.UniqueKey> uniqueKeysFromPopAll = new ArrayList<>();
125+
for (Map.Entry<String, HashSet<String>> uniqueKeyEntry : uniqueKeysHashMap.entrySet()) {
126+
UniqueKeys.UniqueKey uniqueKey = new UniqueKeys.UniqueKey(uniqueKeyEntry.getKey(), new ArrayList<>(uniqueKeyEntry.getValue()));
127+
uniqueKeysFromPopAll.add(uniqueKey);
128+
}
129+
_telemetrySynchronizer.synchronizeUniqueKeys(new UniqueKeys(uniqueKeysFromPopAll));
130+
} finally {
131+
sendGuard.set(false);
119132
}
120-
_telemetrySynchronizer.synchronizeUniqueKeys(new UniqueKeys(uniqueKeysFromPopAll));
121133
}
122134

123135
private interface ExecuteUniqueKeysAction{
@@ -138,4 +150,8 @@ public void execute() {
138150
sendUniqueKeys();
139151
}
140152
}
153+
154+
public AtomicBoolean getSendGuard() {
155+
return sendGuard;
156+
}
141157
}

client/src/main/java/io/split/client/impressions/filters/BloomFilterImp.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
public class BloomFilterImp implements Filter {
88

9-
private BloomFilter bloomFilter;
9+
private BloomFilter<String> bloomFilter;
1010
private final int size;
1111
private final double errorMargin;
1212

@@ -17,17 +17,17 @@ public BloomFilterImp(int size, double errorMargin) {
1717
}
1818

1919
@Override
20-
public synchronized boolean add(String data) {
20+
public boolean add(String data) {
2121
return bloomFilter.put(data);
2222
}
2323

2424
@Override
25-
public synchronized boolean contains(String data) {
25+
public boolean contains(String data) {
2626
return bloomFilter.mightContain(data);
2727
}
2828

2929
@Override
30-
public synchronized void clear() {
30+
public void clear() {
3131
bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_16), size, errorMargin);
3232

3333
}

client/src/main/java/io/split/engine/common/SyncManagerImp.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import static io.split.client.utils.SplitExecutorFactory.buildExecutorService;
3030

3131
public class SyncManagerImp implements SyncManager {
32-
private static final Logger _log = LoggerFactory.getLogger(SyncManager.class);
32+
private static final Logger _log = LoggerFactory.getLogger(SyncManagerImp.class);
3333

3434
private final AtomicBoolean _streamingEnabledConfig;
3535
private final Synchronizer _synchronizer;

client/src/main/java/io/split/engine/sse/AuthApiClientImp.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import static com.google.common.base.Preconditions.checkNotNull;
2323

2424
public class AuthApiClientImp implements AuthApiClient {
25-
private static final Logger _log = LoggerFactory.getLogger(AuthApiClient.class);
25+
private static final Logger _log = LoggerFactory.getLogger(AuthApiClientImp.class);
2626

2727
private final CloseableHttpClient _httpClient;
2828
private final String _target;

client/src/test/java/io/split/client/impressions/UniqueKeysTrackerImpTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public void testStopSynchronization() throws Exception {
9090
TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class);
9191
UniqueKeysTrackerImp uniqueKeysTrackerImp = new UniqueKeysTrackerImp(telemetrySynchronizer, 1, 2, null);
9292
uniqueKeysTrackerImp.start();
93+
Assert.assertFalse(uniqueKeysTrackerImp.getSendGuard().get());
9394
Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key1"));
9495
Assert.assertTrue(uniqueKeysTrackerImp.track("feature1","key2"));
9596
Assert.assertTrue(uniqueKeysTrackerImp.track("feature2","key3"));

pluggable-storage/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<artifactId>java-client-parent</artifactId>
88
<groupId>io.split.client</groupId>
9-
<version>4.11.0</version>
9+
<version>4.11.1</version>
1010
</parent>
1111

1212
<version>2.1.0</version>

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>io.split.client</groupId>
66
<artifactId>java-client-parent</artifactId>
7-
<version>4.11.0</version>
7+
<version>4.11.1</version>
88
<dependencyManagement>
99
<dependencies>
1010
<dependency>

redis-wrapper/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<artifactId>java-client-parent</artifactId>
88
<groupId>io.split.client</groupId>
9-
<version>4.11.0</version>
9+
<version>4.11.1</version>
1010
</parent>
1111
<artifactId>redis-wrapper</artifactId>
1212
<version>3.1.0</version>

0 commit comments

Comments
 (0)